[Nickle] nickle: Branch 'master' - 14 commits
Keith Packard
keithp at keithp.com
Wed Feb 22 03:59:53 PST 2012
Makefile.am | 2
alarm.c | 7 +
builtin-semaphore.c | 4
builtin-toplevel.c | 4
builtin.5c | 1
configure.ac | 2
examples/Makefile.am | 4
examples/skiplist.5c | 222 -------------------------------------------------
examples/sort.5c | 97 ---------------------
execute.c | 8 -
float.c | 18 ---
gamma.5c | 128 ++++++++++++++++++++++++++++
gcd.c | 2
lex.l | 42 +++++++--
list.5c | 78 +++++++++++++++++
main.c | 16 ++-
nickle.h | 6 +
rational.c | 2
scanf.5c | 96 ++++++++++++---------
skiplist.5c | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++
sort.5c | 97 +++++++++++++++++++++
sync.c | 19 ++--
test/Makefile.am | 4
test/scanf.5c | 49 ++++++++++
24 files changed, 727 insertions(+), 412 deletions(-)
New commits:
commit 42aeaa3494c68fee1b1559dde8d99ee52677b361
Author: Keith Packard <keithp at keithp.com>
Date: Thu Feb 23 00:56:41 2012 +1300
Add list.5c
A useful data type
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/list.5c b/list.5c
new file mode 100644
index 0000000..a5df50e
--- /dev/null
+++ b/list.5c
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp at keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+namespace List {
+ public typedef list_t;
+
+ public typedef struct {
+ *list_t prev;
+ *list_t next;
+ } list_t;
+
+ public void init(*list_t l) {
+ l->next = l;
+ l->prev = l;
+ }
+
+ void add(*list_t entry, *list_t prev, *list_t next) {
+ next->prev = entry;
+ entry->next = next;
+ entry->prev = prev;
+ prev->next = entry;
+ }
+
+ void del(*list_t prev, *list_t next) {
+ next->prev = prev;
+ prev->next = next;
+ }
+
+ public bool is_empty(*list_t head) {
+ return head->next == head;
+ }
+
+ public *list_t first(*list_t head) {
+ assert(!is_empty(head), "empty list");
+ return head->next;
+ }
+
+ public *list_t last(*list_t head) {
+ assert(!is_empty(head), "empty list");
+ return head->prev;
+ }
+
+ public void insert(*list_t entry, *list_t head) {
+ add(entry, head, head->next);
+ }
+
+ public void append(*list_t entry, *list_t head) {
+ add(entry, head->prev, head);
+ }
+
+ public void remove(*list_t entry) {
+ del(entry->prev, entry->next);
+ init(entry);
+ }
+
+ public iterate(*list_t head, bool (*list_t) f) {
+ *list_t next;
+ for (*list_t pos = head->next; pos != head; pos = next) {
+ next = pos->next;
+ if (!f(pos))
+ break;
+ }
+ }
+}
commit ab2ed22e6ff90868fbbebaba1e1851f131044555
Author: Keith Packard <keithp at keithp.com>
Date: Thu Feb 23 00:15:02 2012 +1300
add 'millis' function to return a clock in milliseconds.
Useful when doing things with sleep
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/alarm.c b/alarm.c
index 7183535..bd9dcfd 100644
--- a/alarm.c
+++ b/alarm.c
@@ -144,6 +144,13 @@ do_sleep (Value ms)
RETURN (Void);
}
+Value
+do_millis (void)
+{
+ ENTER();
+ RETURN(NewInt(TimeInMs()));
+}
+
ReferencePtr SleepingReference;
static void
diff --git a/builtin-toplevel.c b/builtin-toplevel.c
index 88d2cea..73c6573 100644
--- a/builtin-toplevel.c
+++ b/builtin-toplevel.c
@@ -26,6 +26,10 @@ import_Toplevel_namespace()
" int time ()\n"
"\n"
" Return seconds since Jan 1, 1970 00:00 GMT\n" },
+ { do_millis, "millis", "i", "", "\n"
+ " int millis ()\n"
+ "\n"
+ " Return time in milliseconds\n" },
{ 0 }
};
diff --git a/nickle.h b/nickle.h
index e29fb86..cab7f37 100644
--- a/nickle.h
+++ b/nickle.h
@@ -885,6 +885,7 @@ Value do_Debug_done (void);
Value do_Debug_collect (void);
Value do_Debug_help (void);
Value do_File_mkpipe (void);
+Value do_millis (void);
/* one argument builtins */
Value do_sleep (Value);
commit 67801c2111e9f35afcd1667fb59a72efcfdb5df8
Author: Keith Packard <keithp at keithp.com>
Date: Tue Feb 21 23:50:18 2012 +1300
Add Semaphore::count
Useful for checking current semaphore value without
modifying it.
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/builtin-semaphore.c b/builtin-semaphore.c
index cd6c523..7d0045b 100644
--- a/builtin-semaphore.c
+++ b/builtin-semaphore.c
@@ -29,6 +29,10 @@ import_Semaphore_namespace()
"\n"
" Increment the count in 's' by one.\n"
" If the count is <= 0, wakeup one thread waiting on 's'.\n" },
+ { do_Semaphore_count, "count", "i", "S", "\n"
+ " int count (semaphore s)\n"
+ "\n"
+ " Return current semaphore count\n" },
{ do_Semaphore_test, "test", "b", "S", "\n"
" bool test (semaphore s)\n"
"\n"
diff --git a/nickle.h b/nickle.h
index 09a6056..e29fb86 100644
--- a/nickle.h
+++ b/nickle.h
@@ -925,6 +925,7 @@ Value do_Thread_get_priority (Value);
Value do_Thread_id_to_thread (Value);
Value do_Thread_join (Value);
Value do_Semaphore_signal (Value);
+Value do_Semaphore_count (Value);
Value do_Semaphore_wait (Value);
Value do_Semaphore_test (Value);
Value do_File_close (Value);
diff --git a/sync.c b/sync.c
index cc1d4fd..588c49a 100644
--- a/sync.c
+++ b/sync.c
@@ -39,6 +39,13 @@ do_Semaphore_test (Value s)
}
Value
+do_Semaphore_count (Value s)
+{
+ ENTER();
+ RETURN (NewInt(s->semaphore.count));
+}
+
+Value
do_Semaphore_signal (Value s)
{
ENTER ();
commit 0d6d153498b51bf02286eff06d327ab42914361e
Author: Keith Packard <keithp at keithp.com>
Date: Tue Feb 21 23:49:37 2012 +1300
Clean up do_Semaphore_wait
Make it clear that the semaphore count gets bumped down the first time
into this function.
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/sync.c b/sync.c
index 6bca5cd..cc1d4fd 100644
--- a/sync.c
+++ b/sync.c
@@ -17,14 +17,12 @@ do_Semaphore_wait (Value s)
if (aborting)
RETURN (Void);
if (!running->thread.partial)
- {
--s->semaphore.count;
- if (s->semaphore.count < 0)
- {
- running->thread.partial = 1;
- ThreadSleep (running, s, PrioritySync);
- RETURN (Void);
- }
+ if (s->semaphore.count < 0)
+ {
+ running->thread.partial = 1;
+ ThreadSleep (running, s, PrioritySync);
+ RETURN (Void);
}
complete = True;
RETURN (Void);
commit 6fd77649e822e026000ea5c857d2c30af8874030
Author: Keith Packard <keithp at keithp.com>
Date: Tue Feb 21 23:47:30 2012 +1300
Check for thread switch even if current thread is last
Threads can switch due to semaphores or other signals; that can leave
the current thread last in the run queue. Check for any case where
running changes instead of only when the current thread isn't last.
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/execute.c b/execute.c
index 64f2624..c127cb9 100644
--- a/execute.c
+++ b/execute.c
@@ -1448,13 +1448,9 @@ ThreadsRun (Value thread, Value lex)
inst = next;
thread->thread.continuation.pc = inst;
if (thread->thread.next)
- {
ThreadStepped (thread);
- if (running != thread)
- {
- break;
- }
- }
+ if (running != thread)
+ break;
}
EXIT ();
}
commit 61bd1359a1bc3766833071d04610176a5dfe6b6a
Author: Keith Packard <keithp at keithp.com>
Date: Tue Feb 21 17:42:29 2012 +1300
Make scanf not report valid conversion on blank input.
scanf was incorrectly accepting " " as a valid number, returning a
conversion of 0. Fix this by checking for empty strings in any numeric
conversion.
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/scanf.5c b/scanf.5c
index 83ef351..6fc44c2 100644
--- a/scanf.5c
+++ b/scanf.5c
@@ -7,6 +7,8 @@ extend namespace File {
* According to 'format', read from 'f' to 'args'
*/
{
+ exception bad_conversion();
+
/* Skip whitespace */
void whitespace ()
{
@@ -88,6 +90,8 @@ extend namespace File {
}
s = s + String::new(c);
}
+ if (String::length(s) == 0)
+ raise bad_conversion();
return string_to_integer (s, base);
}
@@ -105,6 +109,8 @@ extend namespace File {
}
s = s + String::new(c);
}
+ if (String::length(s) == 0)
+ raise bad_conversion();
return string_to_real (s);
}
@@ -120,6 +126,8 @@ extend namespace File {
}
s = s + String::new(c);
}
+ if (String::length(s) == 0)
+ raise bad_conversion();
return s;
}
@@ -129,36 +137,55 @@ extend namespace File {
while (i < String::length (format) && !end(f) && !error(f))
{
- switch (format[i]) {
- case ' ':
- case '\t':
- whitespace ();
- break;
- case '%':
- i++;
+ try {
switch (format[i]) {
- case 'b': case 'B':
- *args[argc++] = integer (isbinary, 2);
- break;
- case 'o': case 'O':
- *args[argc++] = integer (isoctal, 8);
- break;
- case 'd': case 'D':
- *args[argc++] = integer (isdecimal, 10);
- break;
- case 'x': case 'X':
- *args[argc++] = integer (ishex, 16);
- break;
- case 'e': case 'E':
- case 'f': case 'F':
- case 'g': case 'G':
- *args[argc++] = number(isfloat);
+ case ' ':
+ case '\t':
+ whitespace ();
break;
- case 'c':
- *args[argc++] = getc(f);
- break;
- case 's':
- *args[argc++] = word();
+ case '%':
+ i++;
+ switch (format[i]) {
+ case 'b': case 'B':
+ *args[argc] = integer (isbinary, 2);
+ argc++;
+ break;
+ case 'o': case 'O':
+ *args[argc] = integer (isoctal, 8);
+ argc++;
+ break;
+ case 'd': case 'D':
+ *args[argc] = integer (isdecimal, 10);
+ argc++;
+ break;
+ case 'x': case 'X':
+ *args[argc] = integer (ishex, 16);
+ argc++;
+ break;
+ case 'e': case 'E':
+ case 'f': case 'F':
+ case 'g': case 'G':
+ *args[argc] = number(isfloat);
+ argc++;
+ break;
+ case 'c':
+ *args[argc] = getc(f);
+ argc++;
+ break;
+ case 's':
+ *args[argc] = word();
+ argc++;
+ break;
+ default:
+ if (end (f))
+ return argc;
+ c = getc(f);
+ if (c != format[i]) {
+ ungetc (c, f);
+ return argc;
+ }
+ break;
+ }
break;
default:
if (end (f))
@@ -170,18 +197,9 @@ extend namespace File {
}
break;
}
- break;
- default:
- if (end (f))
- return argc;
- c = getc(f);
- if (c != format[i]) {
- ungetc (c, f);
- return argc;
- }
- break;
+ i++;
+ } catch bad_conversion() {
}
- i++;
}
return argc;
}
commit e6abc97a91f08a6ca6d06da5f88b627cf6b16a35
Author: Keith Packard <keithp at keithp.com>
Date: Tue Feb 21 17:41:13 2012 +1300
Add tests for scanf function
Scanf incorrectly accepts blank strings for numbers; here's a pile
of tests to validate various numeric input.
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/test/Makefile.am b/test/Makefile.am
index 7ab6cb7..d0ccb6d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,4 +1,6 @@
-check_SCRIPTS=gcdtest.5c \
+check_SCRIPTS=\
+ scanf.5c \
+ gcdtest.5c \
inttest.5c \
optest.5c \
orderofoptest.5c \
diff --git a/test/scanf.5c b/test/scanf.5c
new file mode 100644
index 0000000..feb0bc7
--- /dev/null
+++ b/test/scanf.5c
@@ -0,0 +1,49 @@
+int errors = 0;
+
+void check(string input, string format, int count, poly value) {
+ poly got_value;
+ int got_count = File::fscanf(File::string_read(input), format, &got_value);
+ if (got_count != count) {
+ printf ("got %d wanted %d. input %s format %s\n",
+ got_count, count, input, format);
+ errors++;
+ return;
+ }
+ if (count != 0) {
+ if (got_value != value) {
+ printf ("read %v wanted %v. input %s format %s\n",
+ got_value, value, input, format);
+ errors++;
+ }
+ }
+}
+
+string[] int_formats={ "%b", "%o", "%d", "%x", "%e", "%f", "%g" };
+for (int i = 0; i < dim(int_formats); i++) {
+ check("0", int_formats[i], 1, 0);
+ check(" 0", int_formats[i], 1, 0);
+ check("1", "%x", 1, 1);
+ check(" 1", "%x", 1, 1);
+ check("", "%x", 0, 1);
+ check(" ", "%x", 0, 1);
+}
+
+string[] real_formats={ "%e", "%f", "%g" };
+
+typedef struct {
+ string input;
+ real value;
+} real_t;
+
+real_t[] real_tests = {
+ { .input = ".{3}", .value = 1/3 },
+ { .input = "1.0e-30", value = 1e-30 },
+ { .input = "1e30", .value = 1e30 },
+};
+
+for (int i = 0; i < dim(real_formats); i++) {
+ for (int t = 0; t < dim(real_tests); t++) {
+ check(real_tests[t].input, real_formats[i], 1, real_tests[t].value);
+ }
+}
+exit(errors);
commit 2d3f8dc35a068bac522115d6dfc6f573922040c5
Author: Keith Packard <keithp at keithp.com>
Date: Tue Feb 21 14:32:11 2012 +1300
Add sort and skiplist to standard nickle library
These are too useful to just be examples
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/Makefile.am b/Makefile.am
index 4837cf8..1c543cf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,7 +13,7 @@ NICKLEFILES = builtin.5c math.5c scanf.5c mutex.5c \
arc4.5c prng.5c command.5c abort.5c \
printf.5c history.5c ctype.5c string.5c socket.5c \
file.5c parse-args.5c svg.5c process.5c \
- prime_sieve.5c factorial.5c gamma.5c
+ prime_sieve.5c factorial.5c gamma.5c sort.5c list.5c skiplist.5c
DEBIAN = debian/nickle.install debian/changelog debian/compat \
debian/control debian/copyright debian/rules debian/lintian.override
diff --git a/examples/Makefile.am b/examples/Makefile.am
index d67ebc6..29378e8 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -20,9 +20,7 @@ NICKLEFILES=\
roman.5c \
rsa-demo.5c \
rsa.5c \
- skiplist.5c \
- skiplisttest.5c \
- sort.5c
+ skiplisttest.5c
exampledir=$(pkgdatadir)/examples
diff --git a/examples/skiplist.5c b/examples/skiplist.5c
deleted file mode 100644
index f081d48..0000000
--- a/examples/skiplist.5c
+++ /dev/null
@@ -1,222 +0,0 @@
-/* $Header$ */
-
-/*
- * Copyright © 2004 Keith Packard and Bart Massey.
- * All Rights Reserved. See the file COPYING in this directory
- * for licensing information.
- */
-
-autoload PRNG;
-
-namespace Skiplist {
- public typedef *struct {
- } List;
-
- public typedef bool (poly a, poly b) Greater;
-
- public typedef void (poly a) Visit;
-
- public exception not_found (poly missing);
-
- /*
- * Private representation of an element
- */
- typedef Element;
-
- typedef union {
- *Element element;
- void nil;
- } ElementPtr;
-
- const MaxLevel = 16;
-
- typedef struct {
- poly value;
- ElementPtr[*] forward;
- } Element;
-
- typedef struct {
- int level;
- Greater greater;
- Element header;
- } SkipRec;
-
- typedef *SkipRec Skip;
-
- int random_level ()
- /*
- * This uses a fixed probability of 1/4 for each level
- */
- {
- int bits = PRNG::randbits(MaxLevel * 2);
- int level = 0;
-
- while (++level < MaxLevel)
- {
- if ((bits & 3) != 0)
- break;
- bits >>= 2;
- }
- return level;
- }
-
- public List new (Greater greater)
- /*
- * Allocate a new list with 'greater' as the ordering function
- */
- {
- return &(SkipRec) {
- .level = 0,
- .greater = greater,
- .header = {
- .forward = (ElementPtr[MaxLevel]) { [i] = ElementPtr.nil },
- .value = <>
- }
- };
- }
-
- public poly search (Skip list, poly value)
- /*
- * Search 'list' for 'value', returning a
- * matching value in the list else Raise 'not_found'.
- */
- {
- ElementPtr x = (ElementPtr.element) &list->header;
-
- for (int i = list->level; --i >= 0; )
- {
- while (x.element->forward[i] != ElementPtr.nil &&
- list->greater (value,
- x.element->forward[i].element->value))
- x = x.element->forward[i];
- }
- x = x.element->forward[0];
- if (x == ElementPtr.nil || list->greater (x.element->value, value))
- raise not_found (value);
- return x.element->value;
- }
-
- public void insert (Skip list, poly value)
- /*
- * Insert 'value' into 'list'
- */
- {
- ElementPtr[MaxLevel] update = {};
- ElementPtr x = (ElementPtr.element) &list->header;
-
- for (int i = list->level; --i >= 0;)
- {
- while (x.element->forward[i] != ElementPtr.nil &&
- list->greater (value,
- x.element->forward[i].element->value))
- x = x.element->forward[i];
- update[i] = x;
- }
- x = x.element->forward[0];
- int level = random_level ();
- if (level > list->level)
- {
- level = list->level + 1;
- list->level = level;
- update[level-1] = (ElementPtr.element) &list->header;
- }
-
- /*
- * Allocate new list entry
- */
- ElementPtr new = (ElementPtr.element) &(Element) {
- .value = value,
- .forward = (ElementPtr[level]) {}
- };
-
- for (int i = 0; i < level; i++)
- {
- new.element->forward[i] = update[i].element->forward[i];
- update[i].element->forward[i] = new;
- }
- }
-
- public void delete (Skip list, poly value)
- /*
- * delete entry matching 'value' from 'list', else
- * raise not_found.
- */
- {
- ElementPtr[MaxLevel] update = {};
- ElementPtr x = (ElementPtr.element) &list->header;
-
- for (int i = list->level; --i >= 0;)
- {
- while (x.element->forward[i] != ElementPtr.nil &&
- list->greater (value,
- x.element->forward[i].element->value))
- x = x.element->forward[i];
- update[i] = x;
- }
- x = x.element->forward[0];
- if (x == ElementPtr.nil || list->greater (x.element->value, value))
- raise not_found (value);
-
- for (int i = 0;
- i < list->level && update[i].element->forward[i] == x;
- i++)
- {
- update[i].element->forward[i] = x.element->forward[i];
- }
-
- while (list->level > 0 &&
- list->header.forward[list->level-1] == ElementPtr.nil)
- list->level--;
- }
-
- public void walk (Skip list, Visit visit)
- /*
- * Invoke 'visit' for each element of 'list'.
- * Operations on
- */
- {
- for (ElementPtr e = list->header.forward[0];
- e != ElementPtr.nil;
- e = (ElementPtr next))
- {
- next = e.element->forward[0];
- visit (e.element->value);
- }
- }
-
- public bool (&poly) iterate (Skip list)
- {
- ElementPtr e = list->header.forward[0];
-
- bool next (&poly value) {
- if (e == ElementPtr.nil)
- return false;
- value = e.element->value;
- e = e.element->forward[0];
- return true;
- }
-
- return next;
- }
-
- public int storage (Skip list, poly value)
- {
- ElementPtr x = (ElementPtr.element) &list->header;
-
- for (int i = list->level; --i >= 0;)
- {
- while (x.element->forward[i] != ElementPtr.nil &&
- list->greater (value,
- x.element->forward[i].element->value))
- x = x.element->forward[i];
- }
- x = x.element->forward[0];
- if (x == ElementPtr.nil || list->greater (x.element->value, value))
- raise not_found (value);
- return dim (x.element->forward);
- }
-}
-
-namespace Sortlist {
- public import Skiplist;
-}
diff --git a/examples/sort.5c b/examples/sort.5c
deleted file mode 100644
index dfc3e8d..0000000
--- a/examples/sort.5c
+++ /dev/null
@@ -1,97 +0,0 @@
-/* $Header$
- *
- * Copyright © 2002 Keith Packard and Bart Massey.
- * All Rights Reserved. See the file COPYING in this directory
- * for licensing information.
- */
-
-autoload PRNG;
-
-namespace Sort {
-
- /*
- * Quicksort with random pivot
- */
- public void qsort (&poly[*] a, bool(poly, poly) gt)
- {
- void quicksort (int p, int r) {
- if (p < r) {
- /* swap two array elements */
- void exchange (int i, int j) {
- poly t = a[i]; a[i] = a[j]; a[j] = t;
- }
-
- /* partition the array into two pieces and return the pivot */
- int partition (int p, int r) {
- /* select a random element to pivot */
- int pivot = p + PRNG::randint(p-r);
- exchange (pivot, r);
-
- poly x = a[r];
- int i = p;
- for (int j = p; j < r; j++)
- {
- if (gt (x, a[j]))
- {
- exchange (i, j);
- i++;
- }
- }
- exchange (i, r);
- return i;
- }
-
- int q = partition (p, r);
- quicksort (p, q-1);
- quicksort (q+1, r);
- }
- }
-
- quicksort (0, dim(a)-1);
- }
-
- /*
- * Mergesort
- */
- public void mergesort (&poly[*] a, bool(poly, poly) gt)
- {
- void msort (int p, int r) {
- if (p < r)
- {
- /* merge two sorted lists together */
- void merge (int p, int q, int r)
- {
- /* temporary storage for left half of array */
- int n1 = q - p + 1;
- poly[n1] L;
- for (int i = 0; i < n1; i++)
- L[i] = a[p+i];
-
- /* temporary storage for right half of array */
- int n2 = r - q;
- poly[n2] R;
- for (int i = 0; i < n2; i++)
- R[i] = a[q+i+1];
-
- /* merge two halves back into main array */
- int i = 0, j = 0, k = p;
- while (i < n1 && j < n2)
- a[k++] = gt (L[i], R[j]) ? R[j++] : L[i++];
- while (i < n1)
- a[k++] = L[i++];
- while (j < n2)
- a[k++] = R[j++];
- }
-
- int q = (p + r) // 2;
- msort (p, q);
- msort (q+1, r);
- merge (p, q, r);
- }
- }
- msort (0, dim(a)-1);
- }
-
- protected int[*] randomints (int n, int max) =
- (int[n]) { [i] = PRNG::randint(max) };
-}
diff --git a/skiplist.5c b/skiplist.5c
new file mode 100644
index 0000000..92102f7
--- /dev/null
+++ b/skiplist.5c
@@ -0,0 +1,231 @@
+/* $Header$ */
+
+/*
+ * Copyright © 2004 Keith Packard and Bart Massey.
+ * All Rights Reserved. See the file COPYING in this directory
+ * for licensing information.
+ */
+
+autoload PRNG;
+
+namespace Skiplist {
+ public typedef bool (poly a, poly b) Greater;
+
+ public typedef void (poly a) Visit;
+
+ public exception not_found (poly missing);
+
+ /*
+ * Private representation of an element
+ */
+ typedef Element;
+
+ typedef union {
+ *Element element;
+ void nil;
+ } ElementPtr;
+
+ const MaxLevel = 16;
+
+ typedef struct {
+ poly value;
+ ElementPtr[*] forward;
+ } Element;
+
+ typedef struct {
+ int level;
+ Greater greater;
+ Element header;
+ } SkipRec;
+
+ public typedef *SkipRec Skip;
+
+ int random_level ()
+ /*
+ * This uses a fixed probability of 1/4 for each level
+ */
+ {
+ int bits = PRNG::randbits(MaxLevel * 2);
+ int level = 0;
+
+ while (++level < MaxLevel)
+ {
+ if ((bits & 3) != 0)
+ break;
+ bits >>= 2;
+ }
+ return level;
+ }
+
+ public Skip new (Greater greater)
+ /*
+ * Allocate a new list with 'greater' as the ordering function
+ */
+ {
+ return &(SkipRec) {
+ .level = 0,
+ .greater = greater,
+ .header = {
+ .forward = (ElementPtr[MaxLevel]) { [i] = ElementPtr.nil },
+ .value = <>
+ }
+ };
+ }
+
+ public poly search (Skip list, poly value)
+ /*
+ * Search 'list' for 'value', returning a
+ * matching value in the list else Raise 'not_found'.
+ */
+ {
+ ElementPtr x = (ElementPtr.element) &list->header;
+
+ for (int i = list->level; --i >= 0; )
+ {
+ while (x.element->forward[i] != ElementPtr.nil &&
+ list->greater (value,
+ x.element->forward[i].element->value))
+ x = x.element->forward[i];
+ }
+ x = x.element->forward[0];
+ if (x == ElementPtr.nil || list->greater (x.element->value, value))
+ raise not_found (value);
+ return x.element->value;
+ }
+
+ public void insert (Skip list, poly value)
+ /*
+ * Insert 'value' into 'list'
+ */
+ {
+ ElementPtr[MaxLevel] update = {};
+ ElementPtr x = (ElementPtr.element) &list->header;
+
+ for (int i = list->level; --i >= 0;)
+ {
+ while (x.element->forward[i] != ElementPtr.nil &&
+ list->greater (value,
+ x.element->forward[i].element->value))
+ x = x.element->forward[i];
+ update[i] = x;
+ }
+ x = x.element->forward[0];
+ int level = random_level ();
+ if (level > list->level)
+ {
+ level = list->level + 1;
+ list->level = level;
+ update[level-1] = (ElementPtr.element) &list->header;
+ }
+
+ /*
+ * Allocate new list entry
+ */
+ ElementPtr new = (ElementPtr.element) &(Element) {
+ .value = value,
+ .forward = (ElementPtr[level]) {}
+ };
+
+ for (int i = 0; i < level; i++)
+ {
+ new.element->forward[i] = update[i].element->forward[i];
+ update[i].element->forward[i] = new;
+ }
+ }
+
+ public void delete (Skip list, poly value)
+ /*
+ * delete entry matching 'value' from 'list', else
+ * raise not_found.
+ */
+ {
+ ElementPtr[MaxLevel] update = {};
+ ElementPtr x = (ElementPtr.element) &list->header;
+
+ for (int i = list->level; --i >= 0;)
+ {
+ while (x.element->forward[i] != ElementPtr.nil &&
+ list->greater (value,
+ x.element->forward[i].element->value))
+ x = x.element->forward[i];
+ update[i] = x;
+ }
+ x = x.element->forward[0];
+ if (x == ElementPtr.nil || list->greater (x.element->value, value))
+ raise not_found (value);
+
+ for (int i = 0;
+ i < list->level && update[i].element->forward[i] == x;
+ i++)
+ {
+ update[i].element->forward[i] = x.element->forward[i];
+ }
+
+ while (list->level > 0 &&
+ list->header.forward[list->level-1] == ElementPtr.nil)
+ list->level--;
+ }
+
+ public void walk (Skip list, Visit visit)
+ /*
+ * Invoke 'visit' for each element of 'list'.
+ * Operations on
+ */
+ {
+ for (ElementPtr e = list->header.forward[0];
+ e != ElementPtr.nil;
+ e = (ElementPtr next))
+ {
+ next = e.element->forward[0];
+ visit (e.element->value);
+ }
+ }
+
+ public bool (&poly) iterate (Skip list)
+ {
+ ElementPtr e = list->header.forward[0];
+
+ bool next (&poly value) {
+ if (e == ElementPtr.nil)
+ return false;
+ value = e.element->value;
+ e = e.element->forward[0];
+ return true;
+ }
+
+ return next;
+ }
+
+ public int length (Skip list)
+ {
+ int len = 0;
+ for (ElementPtr e = list->header.forward[0];
+ e != ElementPtr.nil;
+ e = e.element->forward[0])
+ {
+ len++;
+ }
+ return len;
+ }
+
+ public int storage (Skip list, poly value)
+ {
+ ElementPtr x = (ElementPtr.element) &list->header;
+
+ for (int i = list->level; --i >= 0;)
+ {
+ while (x.element->forward[i] != ElementPtr.nil &&
+ list->greater (value,
+ x.element->forward[i].element->value))
+ x = x.element->forward[i];
+ }
+ x = x.element->forward[0];
+ if (x == ElementPtr.nil || list->greater (x.element->value, value))
+ raise not_found (value);
+ return dim (x.element->forward);
+ }
+}
+
+namespace Sortlist {
+ public import Skiplist;
+}
diff --git a/sort.5c b/sort.5c
new file mode 100644
index 0000000..dfc3e8d
--- /dev/null
+++ b/sort.5c
@@ -0,0 +1,97 @@
+/* $Header$
+ *
+ * Copyright © 2002 Keith Packard and Bart Massey.
+ * All Rights Reserved. See the file COPYING in this directory
+ * for licensing information.
+ */
+
+autoload PRNG;
+
+namespace Sort {
+
+ /*
+ * Quicksort with random pivot
+ */
+ public void qsort (&poly[*] a, bool(poly, poly) gt)
+ {
+ void quicksort (int p, int r) {
+ if (p < r) {
+ /* swap two array elements */
+ void exchange (int i, int j) {
+ poly t = a[i]; a[i] = a[j]; a[j] = t;
+ }
+
+ /* partition the array into two pieces and return the pivot */
+ int partition (int p, int r) {
+ /* select a random element to pivot */
+ int pivot = p + PRNG::randint(p-r);
+ exchange (pivot, r);
+
+ poly x = a[r];
+ int i = p;
+ for (int j = p; j < r; j++)
+ {
+ if (gt (x, a[j]))
+ {
+ exchange (i, j);
+ i++;
+ }
+ }
+ exchange (i, r);
+ return i;
+ }
+
+ int q = partition (p, r);
+ quicksort (p, q-1);
+ quicksort (q+1, r);
+ }
+ }
+
+ quicksort (0, dim(a)-1);
+ }
+
+ /*
+ * Mergesort
+ */
+ public void mergesort (&poly[*] a, bool(poly, poly) gt)
+ {
+ void msort (int p, int r) {
+ if (p < r)
+ {
+ /* merge two sorted lists together */
+ void merge (int p, int q, int r)
+ {
+ /* temporary storage for left half of array */
+ int n1 = q - p + 1;
+ poly[n1] L;
+ for (int i = 0; i < n1; i++)
+ L[i] = a[p+i];
+
+ /* temporary storage for right half of array */
+ int n2 = r - q;
+ poly[n2] R;
+ for (int i = 0; i < n2; i++)
+ R[i] = a[q+i+1];
+
+ /* merge two halves back into main array */
+ int i = 0, j = 0, k = p;
+ while (i < n1 && j < n2)
+ a[k++] = gt (L[i], R[j]) ? R[j++] : L[i++];
+ while (i < n1)
+ a[k++] = L[i++];
+ while (j < n2)
+ a[k++] = R[j++];
+ }
+
+ int q = (p + r) // 2;
+ msort (p, q);
+ msort (q+1, r);
+ merge (p, q, r);
+ }
+ }
+ msort (0, dim(a)-1);
+ }
+
+ protected int[*] randomints (int n, int max) =
+ (int[n]) { [i] = PRNG::randint(max) };
+}
commit d9d1cb686aac1709c55884f3c621185cfc94ecaf
Author: Keith Packard <keithp at keithp.com>
Date: Mon Jan 30 19:32:27 2012 -0800
Add gamma function
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/Makefile.am b/Makefile.am
index 9a93ba1..4837cf8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -13,7 +13,7 @@ NICKLEFILES = builtin.5c math.5c scanf.5c mutex.5c \
arc4.5c prng.5c command.5c abort.5c \
printf.5c history.5c ctype.5c string.5c socket.5c \
file.5c parse-args.5c svg.5c process.5c \
- prime_sieve.5c factorial.5c
+ prime_sieve.5c factorial.5c gamma.5c
DEBIAN = debian/nickle.install debian/changelog debian/compat \
debian/control debian/copyright debian/rules debian/lintian.override
diff --git a/builtin.5c b/builtin.5c
index af07fce..318c2d8 100644
--- a/builtin.5c
+++ b/builtin.5c
@@ -119,6 +119,7 @@ library "command.5c";
library "math.5c";
library "prime_sieve.5c"
library "factorial.5c"
+library "gamma.5c"
import Math;
library "scanf.5c";
library "socket.5c";
diff --git a/gamma.5c b/gamma.5c
new file mode 100644
index 0000000..804d961
--- /dev/null
+++ b/gamma.5c
@@ -0,0 +1,128 @@
+
+extend namespace Math {
+
+ public real gamma(real n)
+ /*
+ * Stieljes continuing fraction method of computing
+ * gamma
+ */
+ {
+ real Stieltjes(real n, int ord, int bits) {
+
+ /*
+ * Compute the continuing fraction coefficients
+ */
+ real[*] StieltjesCF(int len, int bits) {
+
+ /* Compute a set of Bernouli numbers
+ * using the Akiyama-Tanigawa algorithm
+ */
+ real[*] AkiyamaTanigawa(int l, int bits) {
+ int n = 2 * l + 1;
+ real[n] t = { imprecise(1, bits) };
+ real[l] a;
+ int k = 1;
+ for (int m = 2; m <= n; m++) {
+ t[m-1] = 1/imprecise(m, bits);
+ for (int j = m-1; j >= 1; j--)
+ t[j-1] = j * (t[j-1] - t[j]);
+ if ((m & 1) != 0) {
+ real rk = imprecise(k, bits);
+ real v = t[0]/((2*rk-1)*(2*rk));
+ if ((k & 1) == 0)
+ a[k-1] = -v;
+ else
+ a[k-1] = v;
+ k++;
+ }
+ }
+ return a;
+ }
+ real[*] s = AkiyamaTanigawa(len, bits);
+ real[len,len] m;
+
+ for (int n = 0; n < len; n++)
+ m[n,0] = 0;
+ for (int n = 0; n < len-1; n++)
+ m[n,1] = s[n+1]/s[n];
+ for (int k = 3; k <= len; k++) {
+ for (int n = 1; n <= len - k + 1; n++) {
+ real a = m[n,k-3];
+ real b = m[n,k-2];
+ real c = m[n-1,k-2];
+ if ((k & 1) != 0)
+ m[n-1,k-1] = a + b - c;
+ else
+ m[n-1,k-1] = a * b / c;
+ }
+ }
+ m[0,0] = s[0];
+ return (real[len]) { [k] = m[0,k] };
+ }
+
+ real N = imprecise(n + 1, bits);
+ real q = N;
+ real[*] c = StieltjesCF(ord, bits);
+ real one = imprecise(1, bits);
+ for (int i = ord; i >= 2; i--)
+ q = N + c[i-1] / q;
+ return sqrt(2 * pi_value(bits)/N) * (N/exp(one)) ** N * exp(one/(12*q));
+ }
+
+ /*
+ * For positive integers, just use factorial
+ */
+ if (is_int(n)) {
+ if (n <= 0)
+ raise invalid_argument("gamma of non-positive integer", 0, n);
+ return (floor(n)-1)!;
+ }
+
+ /*
+ * Use Î(z) = Î(z+1)/z
+ * to solve for negative arguments
+ */
+ if (n < 0) {
+ int i = -floor(n) + 1;
+ real g = gamma(n + i);
+
+ while (i-- > 0) {
+ g = g / n;
+ n++;
+ }
+ return g;
+ }
+
+ n = imprecise(n);
+ int bits = precision(n);
+
+ /*
+ * Smaller numbers take a lot more coefficients to
+ * get the desired number of bits. Make the value
+ * larger, and increase the desired precision to match,
+ * to make the result converge faster.
+ */
+ if (n < 10000) {
+ int new_bits = bits + 14 - floor(log2(n));
+ real n_new = imprecise(n, new_bits) + 10000;
+ real g = gamma(n_new);
+ for (int i = 0; i < 10000; i++) {
+ n_new -= 1;
+ g = g / n_new;
+ }
+ return imprecise(g, bits);
+ }
+
+ /* This is a rough approximation of the
+ * number of coefficients in the fraction
+ * needed to produce the desired precision, it's
+ * good for any value larger than 10000. Larger numbers
+ * could use a smaller number of coefficients, but we
+ * don't know how much smaller
+ */
+ int ord = ceil(bits / 20);
+ return imprecise(Stieltjes(n-1, ord, bits + 20), bits);
+ }
+
+ public real(real n) Î = gamma;
+}
commit 8a2d817b59aa16b0e15309d4372b69e4453cbc40
Author: Keith Packard <keithp at keithp.com>
Date: Mon Jan 30 12:10:56 2012 -0800
Printing rational 0 in 'e' format doesn't need an exponent
Computing a negative exponent requires a non-zero value, so just skip
that if the value is zero
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/rational.c b/rational.c
index e7a48b7..ae1083a 100644
--- a/rational.c
+++ b/rational.c
@@ -776,7 +776,7 @@ RationalDecimalPrint (Value f, Value rv, char format, int base, int width, int p
/*
* Check for small numbers for 'e' format
*/
- if (NaturalLess (r->num, r->den))
+ if (NaturalLess (r->num, r->den) && !NaturalZero(r->num))
{
Natural *quo, *rem;
Natural *mag;
commit 9bdf4cd13fa6e473f23ffa171566ffc296074194
Author: Keith Packard <keithp at keithp.com>
Date: Mon Jan 30 10:02:16 2012 -0800
NaturalGcd must return a Natural* when aborting
It was returning One (an Integer) instead of one_natural;
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/gcd.c b/gcd.c
index 2eaff30..71a4f1c 100644
--- a/gcd.c
+++ b/gcd.c
@@ -977,7 +977,7 @@ NaturalGcd (Natural *u0, Natural *v0)
while (v->length)
{
if (aborting)
- RETURN (One);
+ RETURN (one_natural);
#ifdef DEBUG_GCD
FilePrintf (FileStdout, "u = %n;\n", u);
FilePrintf (FileStdout, "v = %n;\n", v);
commit 478825cfa4a72b7f0de80d8b4e07df1ae2e77c9f
Author: Keith Packard <keithp at keithp.com>
Date: Sun Jan 29 23:26:02 2012 -0800
floor() and ceil() should work on imprecise floats
They should return an approximate integer value instead of raising an exception.
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/float.c b/float.c
index 3f2d2f4..b5b20f0 100644
--- a/float.c
+++ b/float.c
@@ -484,23 +484,7 @@ FloatInteger (Value av)
Natural *mag;
int dist;
- /*
- * Can only reduce floats that are integral
- *
- * Ensure that the precision of the number holds every bit
- * This requires that the precision of the representation
- * be no smaller than length of the numbers plus the
- * number of implied zeros.
- *
- * precision >= length (mant) + exponent
- * precision - length (mant) >= exponent
- * !(precision - length (mant) < exponent)
- *
- * The canonical representation ensures that length <= prec
- */
- if (a->exp->sign == Positive &&
- !NaturalLess (NewNatural (a->prec - FpartLength (a->mant)),
- a->exp->mag))
+ if (a->exp->sign == Positive)
{
mag = a->mant->mag;
dist = NaturalToInt (a->exp->mag);
commit 01b07df6e8929e2ef605327c3403271670eeccf1
Author: Keith Packard <keithp at keithp.com>
Date: Sun Jan 29 22:53:07 2012 -0800
Set version to 2.73 in prepartion for eventually release
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/configure.ac b/configure.ac
index 0482ada..0728551 100644
--- a/configure.ac
+++ b/configure.ac
@@ -6,7 +6,7 @@ dnl for licensing information.
AC_PREREQ([2.68])
-AC_INIT([nickle],[2.72],[http://nickle.org],[nickle])
+AC_INIT([nickle],[2.73],[http://nickle.org],[nickle])
AC_CONFIG_SRCDIR([nickle.h])
AC_CONFIG_HEADERS([config.h])
commit faab094af3dcf9a062ba9b6ec1b6b74ab8291c50
Author: Keith Packard <keithp at keithp.com>
Date: Sun Jan 29 22:50:46 2012 -0800
Only call readline tty cleanup on signal readline is active
If readline isn't active, the cleanup functions tend to make a mess of
the tty state, so don't call them. This really only matters when
handling SIGTSTP.
Signed-off-by: Keith Packard <keithp at keithp.com>
diff --git a/lex.l b/lex.l
index 8e9c76c..147ab45 100644
--- a/lex.l
+++ b/lex.l
@@ -89,6 +89,34 @@ NewLexInput (Value file, Atom name, Bool after, Bool interactive)
EXIT ();
}
+#ifdef HAVE_LIBREADLINE
+
+volatile int stdin_in_readline;
+
+static void
+my_prep_terminal(int meta_flag)
+{
+ stdin_in_readline = 1;
+ rl_prep_terminal(meta_flag);
+}
+
+static void
+my_deprep_terminal(void)
+{
+ rl_deprep_terminal();
+ stdin_in_readline = 0;
+}
+
+static int
+LexGetChar (void);
+
+static int
+ReadlineGetChar (FILE *f)
+{
+ return LexGetChar ();
+}
+#endif
+
ReferencePtr LexInputReference;
void
@@ -96,7 +124,10 @@ LexInit (void)
{
ENTER ();
-#if HAVE_RL_CATCH_SIGNALS
+#if HAVE_LIBREADLINE
+ rl_getc_function = ReadlineGetChar;
+ rl_prep_term_function = my_prep_terminal;
+ rl_deprep_term_function = my_deprep_terminal;
rl_catch_signals = 0;
#endif
LexInputReference = NewReference ((void **) &lexInput);
@@ -180,14 +211,6 @@ LexGetChar (void)
}
}
-#ifdef HAVE_LIBREADLINE
-static int
-ReadlineGetChar (FILE *f)
-{
- return LexGetChar ();
-}
-#endif
-
static Value
prompt (void)
{
@@ -227,7 +250,6 @@ LexGetInteractiveChar (void)
else
break;
}
- rl_getc_function = ReadlineGetChar;
rl_inhibit_completion = 1;
line_base = readline (p);
line = line_base;
diff --git a/main.c b/main.c
index 3baaf01..e20f1dd 100644
--- a/main.c
+++ b/main.c
@@ -166,7 +166,8 @@ intr (int sig)
int ret = write(2,"Double interrupt, exiting\n", 26);
(void) ret;
#if HAVE_RL_CLEANUP_AFTER_SIGNAL
- rl_cleanup_after_signal();
+ if (stdin_in_readline)
+ rl_cleanup_after_signal();
#endif
exit(1);
}
@@ -179,7 +180,9 @@ stop (int sig)
sigset_t set, oset;
#if HAVE_RL_CLEANUP_AFTER_SIGNAL
- rl_cleanup_after_signal();
+ printf ("stop %d\n", stdin_in_readline);
+ if (stdin_in_readline)
+ rl_cleanup_after_signal();
#endif
sigfillset (&set);
sigprocmask (SIG_SETMASK, &set, &oset);
@@ -193,7 +196,8 @@ stop (int sig)
IoStart ();
catchSignal (sig, stop);
#if HAVE_RL_RESET_AFTER_SIGNAL
- rl_reset_after_signal();
+ if (stdin_in_readline)
+ rl_reset_after_signal();
#endif
}
@@ -202,7 +206,8 @@ die (int sig)
{
IoStop ();
#if HAVE_RL_CLEANUP_AFTER_SIGNAL
- rl_cleanup_after_signal();
+ if (stdin_in_readline)
+ rl_cleanup_after_signal();
#endif
_exit (sig);
}
@@ -212,7 +217,8 @@ segv (int sig)
{
IoStop ();
#if HAVE_RL_CLEANUP_AFTER_SIGNAL
- rl_cleanup_after_signal();
+ if (stdin_in_readline)
+ rl_cleanup_after_signal();
#endif
releaseSignal (SIGSEGV);
/* return and reexecute the fatal instruction */
diff --git a/nickle.h b/nickle.h
index a9564ad..09a6056 100644
--- a/nickle.h
+++ b/nickle.h
@@ -746,7 +746,9 @@ Bool LexInteractive (void);
Bool LexResetInteractive (void);
void LexInit (void);
void NewLexInput (Value file, Atom name, Bool after, Bool interactive);
-
+#if HAVE_LIBREADLINE
+extern volatile int stdin_in_readline;
+#endif
int yywrap (void);
void yyerror (char *msg);
More information about the Nickle
mailing list