variable classification al a carte

This commit is contained in:
Mark Thom
2022-11-13 10:13:37 -07:00
committed by Mark
parent 170818759d
commit a66d666bed
8 changed files with 198 additions and 188 deletions

View File

@@ -63,32 +63,30 @@ pub(crate) trait Allocator {
// TODO: wha.. why?? grrr. it drains the VarStatus data from vs (which it owns!)
// into self.bindings and perm_vs after all is computed (i.e. vs.populate_restricting_sets()
// and vs.set_perm_vals(has_deep_cut) have both been called).
/*
fn drain_var_data<'a>(
&mut self,
vs: VariableFixtures<'a>,
vs: VariableFixtures,
num_of_chunks: usize,
) -> VariableFixtures<'a> {
) -> VariableFixtures {
let mut perm_vs = VariableFixtures::new();
for (var, (var_status, cells)) in vs.into_iter() {
for (var, var_status) in vs.into_iter() {
match var_status {
VarStatus::Temp(chunk_num, tvd) => {
self.bindings_mut()
.insert(var.clone(), VarData::Temp(chunk_num, 0, tvd));
if chunk_num + 1 == num_of_chunks {
perm_vs.insert_last_chunk_temp_var(var);
}
.insert(var.clone(), VarAlloc::Temp(chunk_num, 0, tvd));
}
VarStatus::Perm(_) => {
self.bindings_mut().insert(var.clone(), VarData::Perm(0));
perm_vs.insert(var, (var_status, cells));
self.bindings_mut().insert(var.clone(), VarAlloc::Perm(0));
perm_vs.insert(var, var_status);
}
};
}
perm_vs
}
*/
fn get(&self, var: Var) -> RegType {
self.bindings()
@@ -102,8 +100,8 @@ pub(crate) trait Allocator {
fn record_register(&mut self, var: Var, r: RegType) {
match self.bindings_mut().get_mut(&var).unwrap() {
&mut VarData::Temp(_, ref mut s, _) => *s = r.reg_num(),
&mut VarData::Perm(ref mut s) => *s = r.reg_num(),
&mut VarAlloc::Temp(_, ref mut s, _) => *s = r.reg_num(),
&mut VarAlloc::Perm(ref mut s) => *s = r.reg_num(),
}
}
}

View File

@@ -1,7 +1,6 @@
use crate::atom_table::*;
use crate::parser::ast::*;
use crate::{perm_v, temp_v};
use crate::allocator::*;
use crate::arithmetic::*;
use crate::debray_allocator::*;
@@ -22,14 +21,14 @@ use std::cell::Cell;
use std::collections::VecDeque;
#[derive(Debug)]
pub(crate) struct ConjunctInfo<'a> {
pub(crate) perm_vs: VariableFixtures<'a>,
pub(crate) struct ConjunctInfo {
pub(crate) perm_vs: VariableFixtures,
pub(crate) num_of_chunks: usize,
pub(crate) has_deep_cut: bool,
}
impl<'a> ConjunctInfo<'a> {
fn new(perm_vs: VariableFixtures<'a>, num_of_chunks: usize, has_deep_cut: bool) -> Self {
impl ConjunctInfo {
fn new(perm_vs: VariableFixtures, num_of_chunks: usize, has_deep_cut: bool) -> Self {
ConjunctInfo {
perm_vs,
num_of_chunks,
@@ -191,8 +190,8 @@ impl DebrayAllocator {
#[inline(always)]
pub(crate) fn get_binding(&self, name: &Var) -> Option<RegType> {
match self.bindings().get(name) {
Some(&VarData::Temp(_, t, _)) if t != 0 => Some(RegType::Temp(t)),
Some(&VarData::Perm(p)) if p != 0 => Some(RegType::Perm(p)),
Some(&VarAlloc::Temp(_, t, _)) if t != 0 => Some(RegType::Temp(t)),
Some(&VarAlloc::Perm(p)) if p != 0 => Some(RegType::Perm(p)),
_ => None,
}
}
@@ -861,7 +860,7 @@ impl<'b> CodeGenerator<'b> {
fn compile_seq<'a>(
&mut self,
iter: ChunkedIterator<'a>,
conjunct_info: &ConjunctInfo<'a>,
conjunct_info: &ConjunctInfo,
code: &mut Code,
) -> Result<(), CompilationError> {
for (chunk_num, _, terms) in iter.rule_body_iter() {
@@ -925,11 +924,11 @@ impl<'b> CodeGenerator<'b> {
}
}
fn compile_cleanup<'a>(
fn compile_cleanup(
&mut self,
code: &mut Code,
conjunct_info: &ConjunctInfo<'a>,
toc: &'a QueryTerm,
conjunct_info: &ConjunctInfo,
toc: &QueryTerm,
) {
// add a proceed to bookend any trailing cuts.
match toc {
@@ -937,7 +936,7 @@ impl<'b> CodeGenerator<'b> {
code.push(instr!("proceed"));
}
_ => {}
};
}
// perform lco.
let dealloc_index = Self::lco(code);

View File

@@ -4,6 +4,7 @@ use crate::forms::*;
use crate::instructions::*;
use crate::iterators::*;
use bit_set::*;
use indexmap::{IndexMap, IndexSet};
use std::cell::Cell;
@@ -11,15 +12,23 @@ use std::collections::BTreeSet;
use std::mem::swap;
use std::vec::Vec;
// labeled with chunk numbers.
pub(crate) type OccurrenceSet = IndexSet<(GenContext, usize)>;
#[derive(Debug)]
pub(crate) enum VarStatus {
Perm(usize),
Temp(usize, TempVarData), // Perm(chunk_num) | Temp(chunk_num, _)
pub(crate) struct TempVarData {
pub(crate) last_term_arity: usize,
pub(crate) use_set: OccurrenceSet,
pub(crate) no_use_set: BitSet<usize>,
pub(crate) conflict_set: BitSet<usize>,
}
pub(crate) type OccurrenceSet = BTreeSet<(GenContext, usize)>;
#[derive(Debug)]
pub(crate) struct TempVarStatus {
chunk_num: usize,
temp_var_data: TempVarData,
}
// TODO: get ridda this! I think.
// Perm: 0 initially, a stack register once processed.
// Temp: labeled with chunk_num and temp offset (unassigned if 0).
#[derive(Debug)]
@@ -37,21 +46,13 @@ impl VarData {
}
}
#[derive(Debug)]
pub(crate) struct TempVarData {
pub(crate) last_term_arity: usize,
pub(crate) use_set: OccurrenceSet,
pub(crate) no_use_set: BTreeSet<usize>,
pub(crate) conflict_set: BTreeSet<usize>,
}
impl TempVarData {
pub(crate) fn new(last_term_arity: usize) -> Self {
TempVarData {
last_term_arity: last_term_arity,
use_set: BTreeSet::new(),
no_use_set: BTreeSet::new(),
conflict_set: BTreeSet::new(),
use_set: BitSet::new(),
no_use_set: BitSet::new(),
conflict_set: BitSet::new(),
}
}
@@ -68,7 +69,7 @@ impl TempVarData {
pub(crate) fn populate_conflict_set(&mut self) {
if self.last_term_arity > 0 {
let arity = self.last_term_arity;
let mut conflict_set: BTreeSet<usize> = (1..arity).collect();
let mut conflict_set: BitSet<usize> = (1..arity).collect();
for &(_, reg) in self.use_set.iter() {
conflict_set.remove(&reg);
@@ -79,26 +80,26 @@ impl TempVarData {
}
}
type VariableFixture<'a> = (VarStatus, Vec<&'a Cell<VarReg>>);
#[derive(Debug)]
pub(crate) struct VariableFixtures<'a> {
perm_vars: IndexMap<Var, VariableFixture<'a>>,
last_chunk_temp_vars: IndexSet<Var>, // TODO: has no use at all!
pub(crate) struct VariableFixtures {
temp_vars: IndexMap<usize, TempVarStatus>,
last_chunk_temp_vars: IndexSet<usize>, // TODO: has no use at all! remove it.
}
impl<'a> VariableFixtures<'a> {
pub(crate) fn new() -> Self {
VariableFixtures {
perm_vars: IndexMap::new(),
temp_vars: IndexMap::new(),
last_chunk_temp_vars: IndexSet::new(),
}
}
// TODO: get rid of this also.
pub(crate) fn insert(&mut self, var: Var, vs: VariableFixture<'a>) {
self.perm_vars.insert(var, vs);
self.temp_vars.insert(var, vs);
}
// TODO: used?
pub(crate) fn insert_last_chunk_temp_var(&mut self, var: Var) {
self.last_chunk_temp_vars.insert(var);
}
@@ -114,27 +115,26 @@ impl<'a> VariableFixtures<'a> {
// Compute the conflict set of u.
// 1.
let mut use_sets: IndexMap<Var, OccurrenceSet> = IndexMap::new();
let mut use_sets: IndexMap<usize, OccurrenceSet> = IndexMap::new();
for (var, &mut (ref mut var_status, _)) in self.iter_mut() {
if let &mut VarStatus::Temp(_, ref mut var_data) = var_status {
let mut use_set = OccurrenceSet::new();
for (var_gen_index, ref mut var_status) in self.temp_vars.iter_mut() {
let TempVarStatus { ref mut temp_var_data, .. } = var_status;
let mut use_set = OccurrenceSet::new();
swap(&mut var_data.use_set, &mut use_set);
use_sets.insert((*var).clone(), use_set);
}
mem::swap(&mut temp_var_data.use_set, &mut use_set);
use_sets.insert(var_gen_index, use_set);
}
for (u, use_set) in use_sets.drain(..) {
// 2.
for &(term_loc, reg) in use_set.iter() {
if let GenContext::Last(cn_u) = term_loc {
for (ref t, &mut (ref mut var_status, _)) in self.iter_mut() {
if let &mut VarStatus::Temp(cn_t, ref mut t_data) = var_status {
if cn_u == cn_t && u != **t {
if !t_data.uses_reg(reg) {
t_data.no_use_set.insert(reg);
}
for (var_gen_index, ref mut var_status) in self.terms_vars.iter_mut() {
let TempVarStatus { chunk_num, ref mut temp_var_data } = var_status;
if cn_u == chunk_num && u != var_gen_index {
if !temp_var_data.uses_reg(reg) {
temp_var_data.no_use_set.insert(reg);
}
}
}
@@ -142,24 +142,13 @@ impl<'a> VariableFixtures<'a> {
}
// 3.
match self.get_mut(u).unwrap() {
&mut (VarStatus::Temp(_, ref mut u_data), _) => {
u_data.use_set = use_set;
u_data.populate_conflict_set();
}
_ => {}
};
let TempVarStatus { ref mut temp_var_data, ..} = self.temp_vars.get_mut(u).unwrap();
temp_var_data.use_set = use_set;
temp_var_data.populate_conflict_set();
}
}
fn get_mut(&mut self, u: Var) -> Option<&mut VariableFixture<'a>> {
self.perm_vars.get_mut(&u)
}
fn iter_mut(&mut self) -> indexmap::map::IterMut<Var, VariableFixture<'a>> {
self.perm_vars.iter_mut()
}
fn record_temp_info(&mut self, tvd: &mut TempVarData, arg_c: usize, term_loc: GenContext) {
match term_loc {
GenContext::Head | GenContext::Last(_) => {
@@ -169,84 +158,27 @@ impl<'a> VariableFixtures<'a> {
};
}
pub(crate) fn vars_above_threshold(&self, index: usize) -> usize {
let mut var_count = 0;
for &(ref var_status, _) in self.values() {
if let &VarStatus::Perm(i) = var_status {
if i > index {
var_count += 1;
}
}
}
var_count
}
pub(crate) fn mark_vars_in_chunk<I>(&mut self, iter: I, lt_arity: usize, term_loc: GenContext)
where
I: Iterator<Item = TermRef<'a>>,
{
pub(crate) fn mark_temp_var(
&mut self,
generated_var_index: usize,
lvl: Level,
classify_info: &ClassifyInfo,
term_loc: GenContext,
) {
let chunk_num = term_loc.chunk_num();
let mut arg_c = 1;
for term_ref in iter {
if let &TermRef::Var(lvl, cell, ref var) = &term_ref {
let mut status = self.perm_vars.swap_remove(var).unwrap_or((
VarStatus::Temp(chunk_num, TempVarData::new(lt_arity)),
Vec::new(),
));
status.1.push(cell);
match status.0 {
VarStatus::Temp(cn, ref mut tvd) if cn == chunk_num => {
if let Level::Shallow = lvl {
self.record_temp_info(tvd, arg_c, term_loc);
}
}
_ => status.0 = VarStatus::Perm(chunk_num),
};
self.perm_vars.insert(var.clone(), status);
let mut status = self.temp_vars.swap_remove(generated_var_index).unwrap_or_else(|| {
TempVarStatus {
chunk_num,
temp_var_data: TempVarData::new(classify_info.arity),
}
});
if let Level::Shallow = term_ref.level() {
arg_c += 1;
}
if let Level::Shallow = lvl {
self.record_temp_info(&mut status, classify_info.arg_c, term_loc);
}
}
pub(crate) fn into_iter(self) -> indexmap::map::IntoIter<Var, VariableFixture<'a>> {
self.perm_vars.into_iter()
}
fn values(&self) -> indexmap::map::Values<Var, VariableFixture<'a>> {
self.perm_vars.values()
}
pub(crate) fn size(&self) -> usize {
self.perm_vars.len()
}
pub(crate) fn set_perm_vals(&self, has_deep_cuts: bool) {
let mut values_vec: Vec<_> = self
.values()
.filter_map(|ref v| match &v.0 {
&VarStatus::Perm(i) => Some((i, &v.1)),
_ => None,
})
.collect();
values_vec.sort_by_key(|ref v| v.0);
let offset = has_deep_cuts as usize;
for (i, (_, cells)) in values_vec.into_iter().rev().enumerate() {
for cell in cells {
cell.set(VarReg::Norm(RegType::Perm(i + 1 + offset)));
}
}
self.temp_vars.insert(Var::Generated(generated_var_index), status);
}
}

View File

@@ -40,7 +40,6 @@ impl VarPtr {
}
}
#[derive(Debug, Clone)]
pub(crate) enum TermRef<'a> {
AnonVar(Level),

View File

@@ -9,6 +9,7 @@ paper "Compiling Large Disjunctions" to Scryer Prolog.
*/
use crate::atom_table::*;
use crate::fixtures::VariableFixtures;
use crate::forms::*;
use crate::instructions::*;
use crate::iterators::*;
@@ -34,7 +35,7 @@ struct BranchNumber {
impl Default for BranchNumber {
fn default() -> Self {
Self {
branch_num: Rational::from(1 << 10),
branch_num: Rational::from(1 << 63),
delta: Rational::from(1),
}
}
@@ -86,16 +87,19 @@ impl BranchNumber {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VarInfo {
var_ptr: VarPtr,
classify_info: ClassifyInfo,
lvl: Level,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ChunkInfo {
chunk_num: usize,
vars: Vec<VarPtr>, // pointer to incidence
}
impl ChunkInfo {
fn new(chunk_num: usize) -> Self {
ChunkInfo { chunk_num, vars: vec![] }
}
term_loc: GenContext,
// pointer to incidence, term occurrence arity.
vars: Vec<VarInfo>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -140,6 +144,23 @@ enum ChunkType {
Last,
}
impl ChunkType {
#[inline(always)]
fn to_gen_context(self, chunk_num: usize) -> GenContext {
match self {
ChunkType::Head => GenContext::Head,
ChunkType::Mid => GenContext::Mid(chunk_num),
ChunkType::Last => GenContext::Last(chunk_num),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClassifyInfo {
arg_c: usize,
arity: usize,
}
enum TraversalState {
// construct a QueryTerm::Branch with number of disjuncts, reset
// the chunk type to that of the chunk preceding the disjunct.
@@ -199,8 +220,15 @@ pub struct VarRecord {
pub num_occurrences: usize,
}
pub type ClassifyFactResult = (Term, Vec<VarRecord>);
pub type ClassifyRuleResult = (Term, Vec<QueryTerm>, Vec<VarRecord>);
// TODO: already exists a VarData! although it may no longer exist??
// Also, the name is too similar to VarInfo. Think of better names!
pub struct VarData {
pub records: Vec<VarRecord>,
pub fixtures: VariableFixtures,
}
pub type ClassifyFactResult = (Term, VarData);
pub type ClassifyRuleResult = (Term, Vec<QueryTerm>, VarData);
fn merge_branch_seq<Iter: Iterator<Item = BranchInfo>>(branches: Iter) -> BranchInfo {
let mut branch_info = BranchInfo::new(BranchNumber::default());
@@ -238,19 +266,20 @@ fn flatten_into_disjunct(build_stack: &mut Vec<QueryTerm>, preceding_len: usize)
fn term_in_other_chunk(term: &Term) -> Option<bool> {
match term {
Term::Clause(_, name, terms) => Some(!ClauseType::is_inbuilt(name, terms.len())),
Term::Clause(_, name, terms) => Some(!ClauseType::is_inbuilt(*name, terms.len())),
Term::Literal(_, Literal::Atom(atom!("!"))) |
Term::Literal(_, Literal::Char('!')) => Some(false),
Term::Literal(_, Literal::Atom(name)) => Some(!ClauseType::is_inbuilt(name, 0)),
Term::Literal(_, Literal::Atom(name)) => Some(!ClauseType::is_inbuilt(*name, 0)),
Term::Var(..) => Some(true),
_ => None,
}
}
// returns true if the insertion of SetLastChunkType was the final push.
// expects that iter iterates over a conjunct of Terms in reverse order.
fn insert_set_last_chunk_type(
state_stack: &mut Vec<TraversalState>,
iter: impl Iterator<Item = TraversalState>,
mut iter: impl Iterator<Item = TraversalState>,
) -> bool {
let beg = state_stack.len();
let mut idx = beg;
@@ -269,6 +298,7 @@ fn insert_set_last_chunk_type(
if will_break {
state_stack.push(TraversalState::SetLastChunkType);
state_stack.push(traversal_st);
break;
} else {
state_stack.push(traversal_st);
@@ -356,15 +386,22 @@ impl VariableClassifier {
}
fn probe_body_term(&mut self, term: &Term, term_loc: GenContext) {
let mut classify_info = ClassifyInfo { arg_c: 0, arity: term.arity() };
// second arg is true to iterate the root, which may be a variable
for term_ref in breadth_first_iter(term, true) {
if let TermRef::Var(_, _, var_name) = term_ref {
self.probe_body_var(Var::from(var_name), term_loc);
if let TermRef::Var(lvl, _, var_name) = term_ref {
let var_info = VarInfo { var_ptr: VarPtr::from(&var_name), lvl, classify_info };
self.probe_body_var(var_name, term_loc, var_info);
}
if let Level::Shallow = term_ref.level() {
classify_info.arg_c += 1;
}
}
}
fn probe_body_var(&mut self, var_name: Var, chunk_type: ChunkType) {
fn probe_body_var(&mut self, var_name: Var, term_loc: GenContext, var_info: VarInfo) {
let branch_info_v = self.branch_map.entry(var_name)
.or_insert_with(|| vec![]);
@@ -387,11 +424,15 @@ impl VariableClassifier {
};
if needs_new_chunk {
branch_info.chunks.push(ChunkInfo::new(self.current_chunk_num));
branch_info.chunks.push(ChunkInfo {
chunk_num: self.current_chunk_num,
term_loc,
vars: vec![],
});
}
let chunk_info = branch_info.chunks.last_mut().unwrap();
chunk_info.vars.push(VarPtr::from(&var_name));
chunk_info.vars.push(var_info);
}
fn classify_head_variables(&mut self, term: &Term) -> Result<(), CompilationError> {
@@ -401,9 +442,14 @@ impl VariableClassifier {
_ => return Err(CompilationError::InvalidRuleHead),
}
let mut classify_info = ClassifyInfo {
arg_c: 0,
arity: term.arity(),
};
// false argument to breadth_first_iter because the root is not iterable.
for term_ref in breadth_first_iter(term, false) {
if let TermRef::Var(_, _, var_name) = term_ref {
if let TermRef::Var(lvl, _, var_name) = term_ref {
// the body of the if let here is an inlined
// "probe_head_var". note the difference between it
// and "probe_body_var".
@@ -420,11 +466,21 @@ impl VariableClassifier {
let needs_new_chunk = branch_info.chunks.is_empty();
if needs_new_chunk {
branch_info.chunks.push(ChunkInfo::new(self.current_chunk_num));
branch_info.chunks.push(ChunkInfo {
chunk_num: self.current_chunk_num,
term_loc: GenContext::Head,
vars: vec![]
});
}
let chunk_info = branch_info.chunks.last_mut().unwrap();
chunk_info.vars.push(VarPtr::from(&var_name));
let var_info = VarInfo { var_ptr: VarPtr::from(&var_name), classify_info, lvl };
chunk_info.vars.push(var_info);
}
if let Level::Shallow = term_ref.level() {
classify_info.arg_c += 1;
}
}
@@ -584,6 +640,8 @@ impl VariableClassifier {
}
}
Term::Clause(_, atom!(":"), mut terms) if terms.len() == 2 => {
let term_loc = chunk_type.to_gen_context(self.current_chunk_num);
let predicate_name = terms.pop().unwrap();
let module_name = terms.pop().unwrap();
@@ -592,7 +650,7 @@ impl VariableClassifier {
Term::Literal(_, Literal::Atom(module_name)),
Term::Literal(_, Literal::Atom(predicate_name)),
) => {
if !ClauseType::is_inbuilt(name, 0) {
if !ClauseType::is_inbuilt(predicate_name, 0) {
state_stack.push(TraversalState::IncrChunkNum);
}
@@ -649,6 +707,8 @@ impl VariableClassifier {
}
}
Term::Clause(cell, atom!("$call_with_inference_counting"), terms) if terms.len() == 1 => {
let term_loc = chunk_type.to_gen_context(self.current_chunk_num);
for term in terms.iter() {
self.probe_body_term(term, term_loc);
}
@@ -663,6 +723,8 @@ impl VariableClassifier {
state_stack.push(TraversalState::IncrChunkNum);
}
let term_loc = chunk_type.to_gen_context(self.current_chunk_num);
for term in terms.iter() {
self.probe_body_term(term, term_loc);
}
@@ -694,6 +756,7 @@ impl VariableClassifier {
),
);
}
_ => {
return Err(CompilationError::InadmissibleQueryTerm);
}
@@ -707,25 +770,18 @@ impl VariableClassifier {
}
impl BranchMap {
pub fn separate_and_classify_variables(&mut self) -> Vec<VarRecord> {
let mut var_num = 0usize;
let mut records = vec![];
pub fn separate_and_classify_variables(&mut self) -> VarData {
let mut var_num = 0usize;
let mut var_data = VarData {
records: vec![],
fixtures: VariableFixtures::new(),
};
for branches in self.values_mut() {
for branch in branches.iter_mut() {
let mut num_occurrences = 0;
let mut chunk_occurrences = vec![];
for chunk in branch.chunks.iter_mut() {
num_occurrences += chunk.vars.len();
for var in chunk.vars.iter_mut() {
var.set(Var::Generated(var_num));
}
chunk_occurrences.push(chunk.chunk_num);
}
let classification = if branch.chunks.len() > 1 {
VarClassification::Perm
} else {
@@ -739,13 +795,37 @@ impl BranchMap {
.unwrap_or(VarClassification::Void)
};
records.push(VarRecord { classification, chunk_occurrences, num_occurrences });
for chunk in branch.chunks.iter_mut() {
num_occurrences += chunk.vars.len();
if let VarClassification::Temp = classification {
for var_info in chunk.vars.iter_mut() {
var_info.var_ptr.set(Var::Generated(var_num));
var_data.fixtures.mark_temp_var(
var_num,
var_info.lvl,
&var_info.classify_info,
chunk.term_loc,
);
}
} else {
for var_info in chunk.vars.iter_mut() {
var_info.var_ptr.set(Var::Generated(var_num));
}
}
chunk_occurrences.push(chunk.chunk_num);
}
let record = VarRecord { classification, chunk_occurrences, num_occurrences };
var_data.records.push(record);
var_num += 1;
}
}
debug_assert_eq!(records.len(), var_num);
debug_assert_eq!(var_data.records.len(), var_num);
records
var_data
}
}

View File

@@ -227,7 +227,7 @@ macro_rules! perm_v {
};
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum GenContext {
Head,
Mid(usize),