:- non_counted_backtracking univ_errors/3.
univ_errors(Term, List, N) :-
- '$skip_max_list'(N, -1, List, R),
+ '$skip_max_list'(N, _, List, R),
( var(R) ->
( var(Term),
throw(error(instantiation_error, (=..)/2)) % 8.5.3.3 a)
:- meta_predicate parse_options_list(?, 0, ?, ?, ?).
parse_options_list(Options, Selector, DefaultPairs, OptionValues, Stub) :-
- '$skip_max_list'(_, -1, Options, Tail),
+ '$skip_max_list'(_, _, Options, Tail),
( Tail == [] ->
true
; var(Tail) ->
).
must_be_var_names_list(VarNames) :-
- '$skip_max_list'(_, -1, VarNames, Tail),
+ '$skip_max_list'(_, _, VarNames, Tail),
( Tail == [] ->
must_be_var_names_list_(VarNames, VarNames)
; var(Tail) ->
var(List),
!.
can_be_list(List, _) :-
- '$skip_max_list'(_, -1, List, Tail),
+ '$skip_max_list'(_, _, List, Tail),
( var(Tail) ->
true
; Tail == []
).
atom_chars(Atom, List) :-
- '$skip_max_list'(_, -1, List, Tail),
+ '$skip_max_list'(_, _, List, Tail),
( ( Tail == [] ; var(Tail) ) ->
true
; throw(error(type_error(list, List), atom_chars/2))
).
atom_codes(Atom, List) :-
- '$skip_max_list'(_, -1, List, Tail),
+ '$skip_max_list'(_, _, List, Tail),
( ( Tail == [] ; var(Tail) ) ->
true
; throw(error(type_error(list, List), atom_codes/2))
; atom_chars(Atom, AtomChars),
lists:append(BeforeChars, LengthAndAfterChars, AtomChars),
lists:append(LengthChars, AfterChars, LengthAndAfterChars),
- '$skip_max_list'(Before, -1, BeforeChars, []),
- '$skip_max_list'(Length, -1, LengthChars, []),
- '$skip_max_list'(After, -1, AfterChars, []),
+ '$skip_max_list'(Before, _, BeforeChars, []),
+ '$skip_max_list'(Length, _, LengthChars, []),
+ '$skip_max_list'(After, _, AfterChars, []),
atom_chars(Sub_atom, LengthChars)
).
:- meta_predicate foldl(3, ?, ?, ?).
:- meta_predicate foldl(4, ?, ?, ?, ?).
-
-length(Xs, N) :-
- var(N),
- !,
- '$skip_max_list'(M, -1, Xs, Xs0),
- ( Xs0 == [] -> N = M
- ; var(Xs0) -> length_addendum(Xs0, N, M)
- ).
-length(Xs, N) :-
- integer(N),
- N >= 0,
- !,
- '$skip_max_list'(M, N, Xs, Xs0),
- ( Xs0 == [] -> N = M
- ; var(Xs0) -> R is N-M, length_rundown(Xs0, R)
- ).
+length(Xs0, N) :-
+ '$skip_max_list'(M, N, Xs0,Xs),
+ !,
+ ( Xs == [] -> N = M
+ ; nonvar(Xs) -> var(N), throw(error(resource_error(finite_memory),_))
+ ; nonvar(N) -> R is N-M, length_rundown(Xs, R)
+ ; N == Xs -> throw(error(resource_error(finite_memory),_))
+ ; length_addendum(Xs, N, M)
+ ).
length(_, N) :-
- integer(N), !,
- domain_error(not_less_than_zero, N, length/2).
+ integer(N), !,
+ domain_error(not_less_than_zero, N, length/2).
length(_, N) :-
- type_error(integer, N, length/2).
+ type_error(integer, N, length/2).
length_addendum([], N, N).
length_addendum([_|Xs], N, M) :-
% or partial list.
permutation(Xs, Ys) :-
- '$skip_max_list'(Xlen, -1, Xs, XTail),
- '$skip_max_list'(Ylen, -1, Ys, YTail),
+ '$skip_max_list'(Xlen, _, Xs, XTail),
+ '$skip_max_list'(Ylen, _, Ys, YTail),
( XTail == [], YTail == [] % both proper lists
-> Xlen == Ylen
; var(XTail), YTail == [] % partial, proper
self.lam += 1;
if self.tortoise == self.hare {
- return Some(CycleSearchResult::NotList);
+ return Some(CycleSearchResult::Cyclic(self.lam));
} else {
self.teleport_tortoise();
}
let pstr = cell_as_string!(heap[self.hare]);
self.pstr_chars += pstr.as_str_from(n).chars().count();
- CycleSearchResult::PStrLocation(self.num_steps(), n)
+ return CycleSearchResult::PStrLocation(self.num_steps(), n);
}
(HeapCellValueTag::Atom, (name, arity)) => {
- if name == atom!("[]") && arity == 0 {
+ return if name == atom!("[]") && arity == 0 {
CycleSearchResult::ProperList(self.num_steps())
} else {
CycleSearchResult::NotList
- }
+ };
+ }
+ (HeapCellValueTag::Lis, l) => {
+ return CycleSearchResult::UntouchedList(self.num_steps(), l);
}
_ => {
- CycleSearchResult::NotList
+ return CycleSearchResult::NotList;
}
- )
+ );
}
fn add_pstr_chars_and_step(&mut self, heap: &[HeapCellValue], h: usize) -> Option<CycleSearchResult> {
(HeapCellValueTag::PStrLoc, h) => {
return brent_st.add_pstr_chars_and_step(&self.heap, h);
}
- (HeapCellValueTag::PStrOffset) => {
+ (HeapCellValueTag::CStr | HeapCellValueTag::PStrOffset) => {
return brent_st.add_pstr_chars_and_step(&self.heap, brent_st.hare);
}
- (HeapCellValueTag::CStr, cstr_atom) => {
- let cstr = PartialString::from(cstr_atom);
-
- brent_st.pstr_chars += cstr.as_str_from(0).chars().count();
- return Some(CycleSearchResult::ProperList(brent_st.num_steps()));
- }
(HeapCellValueTag::Lis, h) => {
return brent_st.step(h+1);
}
}
pub fn detect_cycles(&self, value: HeapCellValue) -> CycleSearchResult {
- let deref_v = self.deref(value);
- let store_v = self.store(deref_v);
-
+ let store_v = self.store(self.deref(value));
let mut pstr_chars = 0;
let hare = read_heap_cell!(store_v,
}
}
(HeapCellValueTag::Atom, (name, arity)) => {
- if name == atom!("[]") && arity == 0 {
- return CycleSearchResult::EmptyList;
+ return if name == atom!("[]") && arity == 0 {
+ CycleSearchResult::EmptyList
} else {
- return CycleSearchResult::NotList;
- }
+ CycleSearchResult::NotList
+ };
+ }
+ (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => {
+ return CycleSearchResult::PartialList(0, store_v.as_var().unwrap());
}
_ => {
return CycleSearchResult::NotList;
}
pub fn detect_cycles_with_max(&self, max_steps: usize, value: HeapCellValue) -> CycleSearchResult {
- let deref_v = self.deref(value);
- let store_v = self.store(deref_v);
-
+ let store_v = self.store(self.deref(value));
let mut pstr_chars = 0;
let hare = read_heap_cell!(store_v,
if max_steps > 0 {
offset+1
} else {
- return CycleSearchResult::UntouchedList(offset);
+ return CycleSearchResult::UntouchedList(0, offset);
}
}
(HeapCellValueTag::PStrLoc, h) => {
if max_steps > 0 {
s + 2
} else {
- return CycleSearchResult::UntouchedList(s + 1);
+ return CycleSearchResult::UntouchedList(0, s + 1);
}
} else {
return CycleSearchResult::NotList;
CycleSearchResult::NotList
};
}
+ (HeapCellValueTag::AttrVar | HeapCellValueTag::StackVar | HeapCellValueTag::Var) => {
+ return CycleSearchResult::PartialList(0, store_v.as_var().unwrap());
+ }
_ => {
return CycleSearchResult::NotList;
}
brent_st.pstr_chars = pstr_chars;
loop {
- if brent_st.num_steps() == max_steps {
+ if brent_st.num_steps() >= max_steps {
return brent_st.to_result(&self.heap);
}
}
}
- fn finalize_skip_max_list(&mut self, n: usize, value: HeapCellValue) {
- let target_n = self.registers[1];
- self.unify_fixnum(Fixnum::build_with(n as i64), target_n);
+ fn skip_max_list_cycle(&mut self, lam: usize) {
+ fn step(heap: &Heap, mut value: HeapCellValue) -> usize {
+ loop {
+ read_heap_cell!(value,
+ (HeapCellValueTag::PStrLoc, h) => {
+ let (h_offset, _) = pstr_loc_and_offset(&heap, h);
+ return h_offset+1;
+ }
+ (HeapCellValueTag::Lis, h) => {
+ return h+1;
+ }
+ (HeapCellValueTag::Str, s) => {
+ return s+2;
+ }
+ (HeapCellValueTag::AttrVar | HeapCellValueTag::Var, h) => {
+ value = heap[h];
+ }
+ _ => {
+ unreachable!();
+ }
+ );
+ }
+ }
+
+ let mut hare = step(&self.heap, self.registers[3]);
+ let mut tortoise = hare;
+
+ for _ in 0 .. lam {
+ hare = step(&self.heap, self.heap[hare]);
+ }
+
+ while hare != tortoise {
+ hare = step(&self.heap, self.heap[hare]);
+ tortoise = step(&self.heap, self.heap[tortoise]);
+ }
+
+ unify!(self, self.registers[4], self.heap[hare]);
+ }
+
+ fn finalize_skip_max_list(&mut self, n: i64, value: HeapCellValue) {
+ let target_n = self.store(self.deref(self.registers[1]));
+ self.unify_fixnum(Fixnum::build_with(n), target_n);
if !self.fail {
let xs = self.registers[4];
}
}
- fn skip_max_list_result(&mut self, max_steps: Option<i64>) {
- let search_result = if let Some(max_steps) = max_steps {
- if max_steps == -1 {
- self.detect_cycles(self.registers[3])
- } else {
- self.detect_cycles_with_max(max_steps as usize, self.registers[3])
- }
- } else {
+ fn skip_max_list_result(&mut self, max_steps: i64) {
+ let search_result = if max_steps == -1 {
self.detect_cycles(self.registers[3])
+ } else {
+ self.detect_cycles_with_max(max_steps as usize, self.registers[3])
};
match search_result {
CycleSearchResult::PStrLocation(steps, pstr_loc) => {
- self.finalize_skip_max_list(steps, heap_loc_as_cell!(pstr_loc));
+ self.finalize_skip_max_list(steps as i64, pstr_loc_as_cell!(pstr_loc));
}
- CycleSearchResult::UntouchedList(l) => {
- self.finalize_skip_max_list(0, list_loc_as_cell!(l));
+ CycleSearchResult::UntouchedList(n, l) => {
+ self.finalize_skip_max_list(n as i64, list_loc_as_cell!(l));
}
CycleSearchResult::UntouchedCStr(cstr_atom, n) => {
- self.finalize_skip_max_list(n, string_as_cstr_cell!(cstr_atom));
+ self.finalize_skip_max_list(n as i64, string_as_cstr_cell!(cstr_atom));
}
CycleSearchResult::EmptyList => {
self.finalize_skip_max_list(0, empty_list_as_cell!());
}
CycleSearchResult::PartialList(n, r) => {
- self.finalize_skip_max_list(n, r.as_heap_cell_value());
+ self.finalize_skip_max_list(n as i64, r.as_heap_cell_value());
}
CycleSearchResult::ProperList(steps) => {
- self.finalize_skip_max_list(steps, empty_list_as_cell!())
+ self.finalize_skip_max_list(steps as i64, empty_list_as_cell!())
}
CycleSearchResult::NotList => {
- let xs0 = self.registers[3];
- self.finalize_skip_max_list(0, xs0);
+ let n = self.store(self.deref(self.registers[2]));
+
+ self.unify_fixnum(Fixnum::build_with(max_steps), n);
+ self.finalize_skip_max_list(max_steps, self.registers[3]);
+ }
+ CycleSearchResult::Cyclic(lam) => {
+ self.skip_max_list_cycle(lam);
}
};
}
pub fn skip_max_list(&mut self) -> CallResult {
let max_steps = self.store(self.deref(self.registers[2]));
+ let mut max_old = -1i64;
- if max_steps.is_var() {
- let stub = functor_stub(atom!("$skip_max_list"), 4);
- let err = self.instantiation_error();
-
- return Err(self.error_form(err, stub));
- }
-
- let max_steps_n = match Number::try_from(max_steps) {
- Ok(Number::Fixnum(n)) => Some(n.get_num()),
- Ok(Number::Integer(n)) => n.to_i64(),
- _ => None,
- };
+ if !max_steps.is_var() {
+ let max_steps = Number::try_from(max_steps);
- if max_steps_n.map(|i| i >= -1).unwrap_or(false) {
- let n = self.store(self.deref(self.registers[1]));
-
- match Number::try_from(n) {
- Ok(Number::Integer(n)) => {
- if &*n == &0 {
- let xs0 = self.registers[3];
- let xs = self.registers[4];
-
- unify!(self, xs0, xs);
- } else {
- self.skip_max_list_result(max_steps_n);
- }
- }
- Ok(Number::Fixnum(n)) => {
- if n.get_num() == 0 {
- let xs0 = self.registers[3];
- let xs = self.registers[4];
+ let max_steps_n = match max_steps {
+ Ok(Number::Fixnum(n)) => Some(n.get_num()),
+ Ok(Number::Integer(n)) => n.to_i64(),
+ _ => None,
+ };
- unify!(self, xs0, xs);
+ if let Some(max_steps) = max_steps_n {
+ if max_steps.abs() as usize <= 1 << 63 {
+ if max_steps >= 0 {
+ max_old = max_steps;
} else {
- self.skip_max_list_result(max_steps_n);
+ self.fail = true;
+ return Ok(());
}
+ } else if max_steps < 0 {
+ self.fail = true;
+ return Ok(());
}
- _ => {
- self.skip_max_list_result(max_steps_n);
- }
+ } else if !max_steps.map(|n| n.is_integer()).unwrap_or(false) {
+ self.fail = true;
+ return Ok(());
}
- } else {
- let stub = functor_stub(atom!("$skip_max_list"), 4);
- let err = self.type_error(ValidType::Integer, max_steps);
-
- return Err(self.error_form(err, stub));
}
+ self.skip_max_list_result(max_old);
Ok(())
}