aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Lasseter <nat.lasseter@york.ac.uk>2024-01-22 10:23:58 +0000
committerNat Lasseter <nat.lasseter@york.ac.uk>2024-01-22 10:23:58 +0000
commita79414bcf14d17c58e040a7b8524f7bd8f7e5e03 (patch)
tree4f2f232aca568711ecb35caa3444b54f4dfa4361
Migrate from gists
-rw-r--r--NDFA2DFAexample32
-rw-r--r--README36
-rw-r--r--Xmodmap10
-rw-r--r--alleq.hs6
-rw-r--r--box.mzn22
-rw-r--r--cpc/cpc1.rb23
-rw-r--r--cpc/cpc2.rb25
-rw-r--r--describe.rb47
-rw-r--r--digits.rb43
-rw-r--r--gen_object/a_cat.erl13
-rw-r--r--gen_object/gen_object.erl31
-rw-r--r--gen_object/test_a_cat.erl11
-rwxr-xr-xgitmove31
-rw-r--r--logcircle.rb28
-rw-r--r--make.rb314
-rw-r--r--pdfbook4.rb34
-rw-r--r--scan.sh37
-rw-r--r--section.c44
-rw-r--r--slide.rb129
-rw-r--r--stltogl21
-rw-r--r--sudoku.rb156
-rw-r--r--tabelvortoj.rb101
-rw-r--r--todo.vim6
23 files changed, 1200 insertions, 0 deletions
diff --git a/NDFA2DFAexample b/NDFA2DFAexample
new file mode 100644
index 0000000..53b8e77
--- /dev/null
+++ b/NDFA2DFAexample
@@ -0,0 +1,32 @@
+This example from http://en.wikipedia.org/wiki/Powerset_construction
+
+Comment: In the NDFA Extension to the VFSM syntax (VFSMv2N), lambda: and epsilon: are equivalent
+Comment: In VFSMv2N, lambda: and epsilon: are special transitions indicating transition on no input
+Start: 1
+Accept: 3 4
+Edges:
+ 1 0 2
+ 1 lambda: 3
+ 2 1 2
+ 2 1 4
+ 3 epsilon: 2
+ 3 0 4
+ 4 0 3
+End:
+
+Should become a DFA under powerset construction or equivalent
+
+Start: {123}
+Accept: {123} {24} {23} {4}
+Edges:
+ {123} 0 {24}
+ {123} 1 {24}
+ {24} 0 {23}
+ {24} 1 {24}
+ {23} 0 {4}
+ {23} 1 {24}
+ {4} 0 {23}
+ {4} 1 {}
+ {} 0 {}
+ {} 1 {}
+End:
diff --git a/README b/README
new file mode 100644
index 0000000..5ed20e2
--- /dev/null
+++ b/README
@@ -0,0 +1,36 @@
+scan.sh:
+ Batch scanning script
+digits.rb:
+ Digit combinations for target sum
+gen_object/:
+ The start of an erlang OO behaviour
+pdfbook4.rb:
+ Page order to make (or pdfjam command to turn a pdf file into) 4-up double-sided brochure ordered pages (to make two A6 folia per A4 page)
+tabelvortoj.rb:
+ Tabelvortoj test
+cpc/:
+ Cyclically Permutable Codewords
+todo.vim:
+ Simple Vim Todo
+describe.rb:
+ Describe the purpose of an executable
+slide.rb:
+ A laser-cuttable circular slide rule generator
+logcircle.rb:
+ Output cartesian coordinates for the tickmarks along a log scale around a circle
+sudoku.rb:
+ Ruby ILP SAT sudoku solver
+section.c:
+ Like the section command on IOS
+Xmodmap:
+ Swap numbers and symbols on the numrow
+make.rb:
+ Ruby script to make playing The Factory efficient (and thus boring)
+box.mzn:
+ Intended to find the dimensions of a rectangle with an area at least n units and a ratio e.
+NDFA2DFAexample:
+ NDFA to DFA example for VFSM
+stltogl:
+ Vim Script to convert an STL file to openGL glVertex3f()s for inclusion in a c program
+alleq.hs:
+ Haskell function to check all elements of a list are equal
diff --git a/Xmodmap b/Xmodmap
new file mode 100644
index 0000000..5ef003a
--- /dev/null
+++ b/Xmodmap
@@ -0,0 +1,10 @@
+keycode 10 = exclam 1 exclam 1 onesuperior exclamdown onesuperior
+keycode 11 = quotedbl 2 quotedbl 2 twosuperior oneeighth twosuperior
+keycode 12 = sterling 3 sterling 3 threesuperior sterling threesuperior
+keycode 13 = dollar 4 dollar 4 EuroSign onequarter EuroSign
+keycode 14 = percent 5 percent 5 onehalf threeeighths onehalf
+keycode 15 = asciicircum 6 asciicircum 6 threequarters fiveeighths threequarters
+keycode 16 = ampersand 7 ampersand 7 braceleft seveneighths braceleft
+keycode 17 = asterisk 8 asterisk 8 bracketleft trademark bracketleft
+keycode 18 = parenleft 9 parenleft 9 bracketright plusminus bracketright
+keycode 19 = parenright 0 parenright 0 braceright degree braceright
diff --git a/alleq.hs b/alleq.hs
new file mode 100644
index 0000000..7080614
--- /dev/null
+++ b/alleq.hs
@@ -0,0 +1,6 @@
+alleq :: Eq a => [a] -> Maybe a -> Bool
+alleq [] _ = True
+alleq (h:t) Nothing = alleq t (Just h)
+alleq (h:t) (Just e)
+ | h == e = alleq t (Just e)
+ | True = False
diff --git a/box.mzn b/box.mzn
new file mode 100644
index 0000000..0fd6f44
--- /dev/null
+++ b/box.mzn
@@ -0,0 +1,22 @@
+include "globals.mzn";
+
+% Given an integer n and a real e
+par int: n = 566*708;
+par float: e = 16/9;
+
+% Find two integers r <= c
+var int: r; var int: c;
+constraint c >= r;
+output [
+ "Rows: ", show(r), "\n",
+ "Columns: ", show(c), "\n"
+];
+
+% Such that (r-1)c < n <= rc
+constraint
+ ((r-1)*c) < n
+ /\
+ n <= (r*c);
+
+% And the ratio c/r is as close to e as possible.
+solve minimize abs(c/r - e);
diff --git a/cpc/cpc1.rb b/cpc/cpc1.rb
new file mode 100644
index 0000000..1113de0
--- /dev/null
+++ b/cpc/cpc1.rb
@@ -0,0 +1,23 @@
+codewords = []
+
+class Integer
+ def rotate_left(bits, width = bit_width)
+ bits %= width
+ (self << bits | self >> (width - bits)) & ((1 << width) - 1)
+ end
+end
+
+(2**8).times do |candidate|
+ valid_candidate_p = true
+ 8.times do |bits|
+ rotated_candidate = candidate.rotate_left(bits, 8)
+ valid_candidate_p = false if codewords.include?(rotated_candidate)
+ end
+ codewords << candidate if valid_candidate_p
+end
+
+p codewords
+puts codewords.length
+
+#=> [0, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 37, 39, 43, 45, 47, 51, 53, 55, 59, 61, 63, 85, 87, 91, 95, 111, 119, 127, 255]
+#=> 36
diff --git a/cpc/cpc2.rb b/cpc/cpc2.rb
new file mode 100644
index 0000000..54d05c6
--- /dev/null
+++ b/cpc/cpc2.rb
@@ -0,0 +1,25 @@
+BITS = 8
+
+class Integer
+ def rotate_left(bits, width = bit_width)
+ bits %= width
+ (self << bits | self >> (width - bits)) & ((1 << width) - 1)
+ end
+end
+
+candidates = (0...(2**BITS)).to_a
+
+(2**BITS).times do |candidate|
+ next if candidates[candidate].nil?
+ (1...BITS).each do |bits|
+ candidates[candidate.rotate_left(bits, BITS)] = nil
+ end
+end
+
+codewords = candidates.compact
+
+p codewords
+puts codewords.length
+
+#=> [1, 3, 5, 7, 9, 11, 13, 15, 19, 21, 23, 25, 27, 29, 31, 37, 39, 43, 45, 47, 53, 55, 59, 61, 63, 87, 91, 95, 111, 127]
+#=> 30
diff --git a/describe.rb b/describe.rb
new file mode 100644
index 0000000..38fabf9
--- /dev/null
+++ b/describe.rb
@@ -0,0 +1,47 @@
+#!/usr/bin/env ruby
+
+#!DESCRIBE: Describe the purpose of an executable
+
+require 'ptools'
+
+def fatal(filename, msg)
+ $stderr.puts("[ERR] #{filename}: #{msg}")
+ exit 1
+end
+
+filename = ARGV.shift
+
+fatal("???", "File name not given.") if filename.nil?
+
+unless File.exist?(filename)
+ ENV["PATH"].split(?:).each do |path|
+ test = File.join(path, filename)
+ if File.exist?(test)
+ filename = test
+ break
+ end
+ end
+end
+
+basename = File.basename(filename)
+
+fatal(basename, "File does not exist.") unless File.exist?(filename)
+fatal(basename, "File is not executable.") unless File.executable?(filename)
+fatal(basename, "File is binary.") if File.binary?(filename)
+
+file = File.readlines(filename)
+
+describelines = file.select { |line|
+ line =~ /\A[#\/]*!DESCRIBE:/
+ }.map { |line|
+ line.split("!DESCRIBE:")[1].strip
+ }
+
+fatal(basename, "File is indescribable!") if describelines.empty?
+
+puts "#{basename}: #{describelines[0]}"
+describelines[1..-1].each { |line|
+ puts "#{" " * (basename.length + 2)}#{line}"
+}
+
+#!DESCRIBE: Usage: describe FILE
diff --git a/digits.rb b/digits.rb
new file mode 100644
index 0000000..addc51e
--- /dev/null
+++ b/digits.rb
@@ -0,0 +1,43 @@
+#!/usr/bin/env ruby
+
+require 'optimist'
+
+opts = Optimist::options do
+ version "digits 0.2 (c) 2023 Nat Lasseter"
+ banner <<-EOS
+Digits gives valid combinations of digits 1-9 that sum to the given total
+
+Usage:
+ digits [opts]
+
+where [opts] are:
+EOS
+
+ opt :number, "The number of digits", type: :integer
+ opt :target, "The target sum", type: :integer
+ opt :exclude, "Exclude a digit", type: :integer, multi: true
+ opt :include, "Include a digit", type: :integer, multi: true
+
+ educate_on_error
+end
+
+digits = [1,2,3,4,5,6,7,8,9]
+opts[:exclude].each { |i| digits.delete(i) }
+
+def puts_group(digits, mandatory, number, target = nil)
+ if target.nil?
+ puts digits.combination(number).select { |a| (mandatory - a).empty? }.sort { |a, b| a.sum <=> b.sum }.map { |a| "#{a.join} = #{a.sum}" }
+ else
+ puts digits.combination(number).select { |a| a.sum == target }.select { |a| (mandatory - a).empty? }.map { |a| "#{a.join} = #{target}" }
+ end
+end
+
+if opts[:number_given]
+ puts "== #{opts[:number]} =="
+ puts_group(digits, opts[:include], opts[:number], opts[:target])
+else
+ (2..8).each do |n|
+ puts "== #{n} =="
+ puts_group(digits, opts[:include], n, opts[:target])
+ end
+end
diff --git a/gen_object/a_cat.erl b/gen_object/a_cat.erl
new file mode 100644
index 0000000..3e3b00f
--- /dev/null
+++ b/gen_object/a_cat.erl
@@ -0,0 +1,13 @@
+% This is a cat. It behaves like a generic object.
+
+-module(a_cat).
+-behaviour(gen_object).
+-export([initialise/1, handle_message/3]).
+
+initialise([Name]) ->
+ {Name, "Meow"}.
+
+handle_message(say, [Word], {Name, Word}) ->
+ {Word, {Name, Word}};
+handle_message(say, _, State) ->
+ {"", State}.
diff --git a/gen_object/gen_object.erl b/gen_object/gen_object.erl
new file mode 100644
index 0000000..1990ec2
--- /dev/null
+++ b/gen_object/gen_object.erl
@@ -0,0 +1,31 @@
+% This is the behaviour definition for a generic object.
+
+-module(gen_object).
+-export([new/2, delete/1, send/3]).
+
+-callback initialise(Args :: [any()]) -> State :: any().
+-callback handle_message(Msg :: atom(), Args :: [any()], State :: any()) -> {Response :: any(), New_State :: any()}.
+
+send(Obj, Msg, Args) ->
+ Obj ! {self(), Msg, Args},
+ receive
+ Response ->
+ Response
+ end.
+
+new(CBM, Args) ->
+ State = apply(CBM, initialise, [Args]),
+ spawn(fun() -> loop(CBM, State) end).
+
+loop(CBM, State) ->
+ receive
+ gen_object__bif__exit ->
+ ok;
+ {From, Msg, Args} ->
+ {Resp, New_State} = apply(CBM, handle_message, [Msg, Args, State]),
+ From ! Resp,
+ loop(CBM, New_State)
+ end.
+
+delete(Obj) ->
+ Obj ! gen_object__bif__exit.
diff --git a/gen_object/test_a_cat.erl b/gen_object/test_a_cat.erl
new file mode 100644
index 0000000..589bb97
--- /dev/null
+++ b/gen_object/test_a_cat.erl
@@ -0,0 +1,11 @@
+% This is where we test the cat to make sure that it does not bark.
+
+-module(test_a_cat).
+-export([main/0]).
+
+main() ->
+ MyCat = gen_object:new(a_cat, ["Fred"]),
+ FredSays = gen_object:send(MyCat, say, ["Woof"]),
+ gen_object:delete(MyCat),
+
+ io:format("Fred says: ~p~n", [FredSays]).
diff --git a/gitmove b/gitmove
new file mode 100755
index 0000000..90a943d
--- /dev/null
+++ b/gitmove
@@ -0,0 +1,31 @@
+#!/usr/bin/env ruby
+#!DESCRIBE: Migrate repos to gitolite
+
+iam = "me"
+from = "git@github.com:Me/"
+to = "git@gitolite:"
+repos = %w( ... )
+
+File.open("repos.conf", ?w) do |f|
+ repos.each do |r|
+ f.puts "repo #{r}"
+ f.puts " RW+ = #{iam}"
+ end
+end
+
+File.open("repos.script", ?w) do |f|
+ f.puts "#!/bin/bash"
+ repos.each do |r|
+ f.puts "git clone --bare #{from}#{r} __repo_#{r}"
+ f.puts "cd __repo_#{r}"
+ f.puts "git push --all #{to}#{r}"
+ f.puts "git push --tags #{to}#{r}"
+ f.puts "cd .."
+ f.puts "rm -rf __repo_#{r}"
+ f.puts
+ end
+end
+
+File.chmod(0744, "repos.script")
+
+puts "Done generating; now push the contents of repos.conf to your gitolite config, then run the repos.script script."
diff --git a/logcircle.rb b/logcircle.rb
new file mode 100644
index 0000000..95d57ac
--- /dev/null
+++ b/logcircle.rb
@@ -0,0 +1,28 @@
+#!/usr/bin/env ruby
+
+Maths = Math
+
+Intervals = ARGV.shift&.to_i || 2
+Radius = ARGV.shift&.to_i || 25
+Decimals = ARGV.shift&.to_i || 3
+
+Scale = Maths::PI * 2 / Intervals
+Width = 1 + Maths.log10(Radius).to_i + 1 + 1 + Decimals
+
+def a_tick(mantissa, exponent)
+ tick = mantissa * (10 ** exponent)
+ loc = Scale * (exponent + Maths.log10(mantissa))
+ x = Radius * Maths.cos(loc)
+ y = Radius * Maths.sin(loc)
+ puts "%#{Intervals + 1}d: (%4.2fr) %#{Width}.#{Decimals}f %#{Width}.#{Decimals}f" % [tick, loc, x, y]
+end
+
+puts "Max #{10 ** Intervals} (#{Intervals} intervals), radius #{Radius}, #{Decimals} decimals"
+
+Intervals.times do |exponent|
+ (1..9).each do |mantissa|
+ a_tick(mantissa, exponent)
+ end
+end
+
+a_tick(1, Intervals)
diff --git a/make.rb b/make.rb
new file mode 100644
index 0000000..a066927
--- /dev/null
+++ b/make.rb
@@ -0,0 +1,314 @@
+#!/usr/bin/env ruby
+
+require 'json'
+Products = JSON.parse(DATA.read)
+Productindex = Products.keys
+
+ingredientsstack = []
+buildstack = {}
+
+build = ARGV.shift
+if build.nil? then
+ puts "Build what?"
+ exit 1
+end
+
+quantity = ARGV.shift
+if quantity.nil? then
+ quantity = 1
+else
+ quantity = quantity.to_i
+end
+
+ingredientsstack.push build
+
+while ingredientsstack.count > 0 do
+ build = ingredientsstack.pop
+ product = Products[build]
+ if product.nil? then
+ puts "#{build} is not a product"
+ exit 1
+ end
+ product.each do |key, value|
+ value.times do
+ ingredientsstack.push key
+ end
+ end
+ if buildstack[build].nil? then
+ buildstack[build] = 1
+ else
+ buildstack[build] += 1
+ end
+end
+
+buildstack.keys.sort { |a, b|
+ Productindex.index(a) <=> Productindex.index(b)
+}.each do |key|
+ puts "%4d: %s" % [buildstack[key] * quantity, key]
+end
+
+__END__
+{
+ "Cardboard": {},
+ "Concrete": {},
+ "Magnet": {},
+ "Metal": {},
+ "Paint": {},
+ "Plastic": {},
+ "Rare metal": {},
+ "Rubber": {},
+ "Wire": {},
+
+ "Adv concrete": {
+ "Concrete": 2,
+ "Metal": 2
+ },
+ "Belt": {
+ "Rubber": 2
+ },
+ "Box": {
+ "Cardboard": 4
+ },
+ "Cable": {
+ "Metal": 1
+ },
+ "Circuit": {
+ "Plastic": 2,
+ "Wire": 1
+ },
+ "Gear": {
+ "Metal": 2
+ },
+ "Hose": {
+ "Rubber": 2
+ },
+ "Metal wheel": {
+ "Metal": 2
+ },
+ "Motor": {
+ "Magnet": 2,
+ "Metal": 1,
+ "Wire": 2
+ },
+ "Plastic wheel": {
+ "Plastic": 2
+ },
+ "Pump": {
+ "Metal": 2,
+ "Plastic": 1,
+ "Rubber": 1
+ },
+ "Garden gnome": {
+ "Concrete": 1,
+ "Paint": 1,
+ "Box": 1
+ },
+ "Speakers": {
+ "Magnet": 2,
+ "Plastic": 2,
+ "Wire": 1,
+ "Box": 1
+ },
+ "Toaster": {
+ "Metal": 2,
+ "Wire": 2,
+ "Box": 1
+ },
+ "Air gun": {
+ "Metal": 4,
+ "Hose": 2
+ },
+ "Arm": {
+ "Metal": 2,
+ "Circuit": 1,
+ "Motor": 2
+ },
+ "Conveyor": {
+ "Belt": 1,
+ "Gear": 2,
+ "Metal wheel": 8,
+ "Motor": 1
+ },
+ "Lifter": {
+ "Metal": 3,
+ "Cable": 2,
+ "Hose": 2,
+ "Motor": 1,
+ "Pump": 1
+ },
+ "Logic unit": {
+ "Wire": 5,
+ "Circuit": 4
+ },
+ "Mover": {
+ "Metal": 3,
+ "Gear": 4,
+ "Metal wheel": 4,
+ "Motor": 2
+ },
+ "Road": {
+ "Concrete": 4,
+ "Adv concrete": 2
+ },
+ "Support": {
+ "Metal": 2,
+ "Adv concrete": 2
+ },
+ "Thing-a-ma-jig": {
+ "Circuit": 3,
+ "Hose": 2,
+ "Motor": 1,
+ "Pump": 2
+ },
+ "Widget": {
+ "Metal": 1,
+ "Plastic": 4,
+ "Wire": 2,
+ "Circuit": 1
+ },
+ "Toy car": {
+ "Metal": 1,
+ "Paint": 1,
+ "Plastic": 3,
+ "Plastic wheel": 4,
+ "Box": 1
+ },
+ "Water gun": {
+ "Paint": 1,
+ "Plastic": 6,
+ "Hose": 2,
+ "Box": 1
+ },
+
+ "Adv logic unit": {
+ "Wire": 4,
+ "Circuit": 4,
+ "Logic unit": 2
+ },
+ "Assembly line": {
+ "Air gun": 2,
+ "Arm": 2,
+ "Conveyor": 3,
+ "Lifter": 1,
+ "Mover": 1
+ },
+ "Jet engine": {
+ "Metal": 8,
+ "Wire": 12,
+ "Hose": 6,
+ "Pump": 4,
+ "Thing-a-ma-jig": 1
+ },
+ "Sensor": {
+ "Rare metal": 2,
+ "Wire": 1,
+ "Circuit": 1,
+ "Logic unit": 1
+ },
+ "Bridge": {
+ "Road": 6,
+ "Support": 6
+ },
+ "Forklift": {
+ "Metal": 6,
+ "Rubber": 8,
+ "Metal wheel": 4,
+ "Motor": 2,
+ "Box": 4
+ },
+ "Radio tower": {
+ "Metal": 12,
+ "Wire": 6,
+ "Support": 4
+ },
+ "Tablet computer": {
+ "Plastic": 1,
+ "Wire": 3,
+ "Circuit": 3,
+ "Logic unit": 1,
+ "Box": 1
+ },
+
+ "Drone": {
+ "Plastic": 4,
+ "Rare metal": 1,
+ "Motor": 4,
+ "Adv logic unit": 1,
+ "Sensor": 2
+ },
+ "Jet": {
+ "Metal": 24,
+ "Wire": 18,
+ "Adv logic unit": 6,
+ "Jet engine": 4,
+ "Sensor": 8
+ },
+ "Oculus rift": {
+ "Plastic": 2,
+ "Rare metal": 2,
+ "Wire": 4,
+ "Widget": 2,
+ "Sensor": 2
+ },
+
+ "Builder: Tier 1": {
+ "Motor": 1,
+ "Arm": 2,
+ "Conveyor": 1,
+ "Thing-a-ma-jig": 1
+ },
+ "Builder: Tier 2": {
+ "Motor": 1,
+ "Arm": 3,
+ "Conveyor": 1,
+ "Thing-a-ma-jig": 1,
+ "Widget": 2
+ },
+ "Builder: Tier 3": {
+ "Logic unit": 1,
+ "Thing-a-ma-jig": 1,
+ "Widget": 4,
+ "Assembly line": 1
+ },
+ "Builder: Tier 4": {
+ "Widget": 2,
+ "Adv logic unit": 2,
+ "Assembly line": 1,
+ "Sensor": 6
+ },
+ "Builder: Builders": {
+ "Arm": 2,
+ "Logic unit": 1,
+ "Thing-a-ma-jig": 4,
+ "Widget": 1,
+ "Assembly line": 1
+ },
+ "Builder: Taskers": {
+ "Thing-a-ma-jig": 2,
+ "Widget": 2,
+ "Adv logic unit": 1,
+ "Assembly line": 1
+ },
+ "Builder: Universal": {
+ "Arm": 4,
+ "Thing-a-ma-jig": 2,
+ "Widget": 4,
+ "Adv logic unit": 2,
+ "Assembly line": 2
+ },
+
+ "Tasker: Purchasing": {
+ "Paint": 1,
+ "Wire": 6,
+ "Circuit": 4,
+ "Logic unit": 1,
+ "Widget": 1
+ },
+ "Tasker: Sales": {
+ "Paint": 1,
+ "Wire": 8,
+ "Logic unit": 2,
+ "Widget": 1,
+ "Adv logic unit": 1
+ }
+}
diff --git a/pdfbook4.rb b/pdfbook4.rb
new file mode 100644
index 0000000..e7f66d2
--- /dev/null
+++ b/pdfbook4.rb
@@ -0,0 +1,34 @@
+#!/usr/bin/env ruby
+
+n = ARGV.shift.to_i
+m = n
+m = ((m / 8) + 1) * 8 if m % 8 != 0
+
+p = (1..n).map(&:to_s) + ["{}"] * (m - n)
+o = []
+
+until p.empty? do
+ t1 = []
+ t2 = []
+
+ t1.push(p.pop)
+ t1.push(p.shift)
+ t2.push(p.shift)
+ t2.push(p.pop)
+
+ t1.push(p.pop)
+ t1.push(p.shift)
+ t2.push(p.shift)
+ t2.push(p.pop)
+
+ o += t1
+ o += t2
+end
+
+f = ARGV.shift
+
+if f.nil?
+ puts o.join(?,)
+else
+ puts "pdfjam --nup 2x2 -o #{File.basename(f, ".*")}-book.pdf #{f} #{o.join(?,)}"
+end
diff --git a/scan.sh b/scan.sh
new file mode 100644
index 0000000..ecb505c
--- /dev/null
+++ b/scan.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+function scanpage {
+ echo -n insert $1 and press return
+ read
+ scanimage \
+ -d 'brother5:bus2;dev2' \
+ --AutoDocumentSize=yes \
+ --format=pdf \
+ --resolution 300 \
+ --AutoDeskew=yes \
+ -o $1 2>/dev/null
+}
+
+if [[ -z $1 ]] ; then
+ echo -n 'prefix ?'
+ read pref
+ [[ -z $pref ]] && exit
+else
+ pref=$1
+ shift
+fi
+
+if [[ -z $1 ]] ; then
+ while true ; do
+ echo -n 'document#(s) ?'
+ read n
+ [[ -z $n ]] && exit
+ for j in $n ; do
+ scanpage ${pref}-$j.pdf
+ done
+ done
+else
+ for j in $* ; do
+ scanpage ${pref}-$j.pdf
+ done
+fi
diff --git a/section.c b/section.c
new file mode 100644
index 0000000..eaefcbd
--- /dev/null
+++ b/section.c
@@ -0,0 +1,44 @@
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char** argv) {
+ regex_t* preg;
+ int reti;
+ char* linebuf;
+ char insection;
+
+ if(argc != 2) {
+ fprintf(stderr, "Usage: section <REGEX>\n");
+ return 1;
+ }
+
+ reti = regcomp(preg, argv[1], REG_EXTENDED);
+ if (reti) {
+ fprintf(stderr, "Could not compile regex\n");
+ return 1;
+ }
+
+ linebuf = (char*) calloc(4096, sizeof(char));
+ insection = 0;
+
+ while (fgets(linebuf, 4096, stdin) != NULL) {
+ if (insection) {
+ if (linebuf[0] != ' ' && linebuf[0] != '\t') {
+ insection = 0;
+ } else
+ fprintf(stdout, "%s", linebuf);
+ } else {
+ if (linebuf[0] != ' ' && linebuf[0] != '\t') {
+ reti = regexec(preg, linebuf, 0, NULL, 0);
+ if (!reti) {
+ fprintf(stdout, "---\n%s", linebuf);
+ insection = 1;
+ }
+ }
+ }
+ }
+
+ free(linebuf);
+ regfree(preg);
+}
diff --git a/slide.rb b/slide.rb
new file mode 100644
index 0000000..46505aa
--- /dev/null
+++ b/slide.rb
@@ -0,0 +1,129 @@
+#!/usr/bin/env ruby
+
+Maths = Math
+
+Radius = ARGV.shift&.to_f || 50.0
+Ring = ARGV.shift&.to_f || 15.0
+
+Decimals = 6
+Origin_x = Radius + Ring + 1
+Origin_y = Radius + Ring + 1
+
+Page_x = 2 * 2 * Origin_x
+Page_y = 2 * Origin_y
+
+def a_preamble(page_x, page_y)
+ puts <<-EOT % [page_x, page_y, page_x, page_y]
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+
+<svg
+ width="%dmm"
+ height="%dmm"
+ viewBox="0 0 %d %d"
+ version="1.1"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg"
+>
+ <g>
+ EOT
+end
+
+def a_postamble
+ puts <<-EOT
+ </g>
+</svg>
+ EOT
+end
+
+def a_circle(origin_x, origin_y, radius)
+ puts <<-EOT % [origin_x, origin_y, radius, radius]
+ <ellipse
+ style="fill:none;stroke:#FF0000;stroke-width:0.1"
+ cx="%.6f"
+ cy="%.6f"
+ rx="%.6f"
+ ry="%.6f"
+ />
+ EOT
+end
+
+def a_mark(xf, yf, xt, yt)
+ puts <<-EOT % [xf, yf, xt, yt]
+ <path
+ style="fill:none;stroke:#0000FF;stroke-width:0.1"
+ d="M %.6f,%.6f %.6f,%.6f"
+ />
+ EOT
+end
+
+def a_label(origin_x, origin_y, radius, heading, mod, ticktext)
+ xt = origin_x + (radius * Maths.cos(heading) * (1 + mod))
+ yt = origin_y + (radius * Maths.sin(heading) * (1 + mod))
+ puts <<-EOT % [xt, yt, ticktext]
+ <text
+ xml:space="preserve"
+ style="fill:#000000;stroke:none;font-size:1.5px;font-family:'Google Sans'"
+ x="%.6f"
+ y="%.6f"
+ text-anchor="middle"
+ >%s</text>
+ EOT
+end
+
+def a_double_label(origin_x, origin_y, radius, heading, mod, ticktext)
+ a_label(origin_x, origin_y, radius, heading, -mod, ticktext)
+ a_label(origin_x, origin_y, radius, heading, mod, ticktext)
+end
+
+def a_tick(mantissa, exponent, origin_x, origin_y, radius, intervals)
+ scale = Maths::PI * 2 / intervals
+ (0..0.9).step(0.1).each do |sub|
+ tick = mantissa * (10 ** exponent) + sub
+ heading = scale * (exponent + Maths.log10(mantissa + sub))
+
+ adj = case mantissa + sub
+ when *[1.0,5.0]
+ 0.06
+ when *[2.0,3.0,4.0,6.0,7.0,8.0,9.0]
+ 0.04
+ else
+ 0.02
+ end
+ xi = origin_x + (radius * Maths.cos(heading) * (1 - adj))
+ yi = origin_y + (radius * Maths.sin(heading) * (1 - adj))
+ xo = origin_x + (radius * Maths.cos(heading) * (1 + adj))
+ yo = origin_y + (radius * Maths.sin(heading) * (1 + adj))
+
+ ticktext = if tick == 1.0
+ "#{tick.to_i}(#{"0" * intervals})"
+ else
+ "#{tick.to_i}"
+ end
+
+ a_mark(xi, yi, xo, yo)
+ a_double_label(origin_x, origin_y, radius, heading, adj + 0.04, ticktext) if sub == 0
+ end
+end
+
+def a_scale(origin_x, origin_y, radius, intervals)
+ intervals.times do |exponent|
+ (1..9).each do |mantissa|
+ a_tick(mantissa, exponent, origin_x, origin_y, radius, intervals)
+ end
+ end
+end
+
+a_preamble(Page_x + 1, Page_y + 1)
+
+a_circle(Origin_x, Origin_y, Radius)
+a_circle(Origin_x, Origin_y, Radius + Ring)
+a_circle(Origin_x + (2 * (Radius + Ring + 1)), Origin_y, Radius + Ring)
+a_circle(Origin_x, Origin_y, Radius - Ring)
+
+a_double_label(Origin_x, Origin_y, Radius - Ring, 0.05, 0.1, "x")
+a_scale(Origin_x, Origin_y, Radius - Ring, 1)
+
+a_double_label(Origin_x, Origin_y, Radius, 0.04, 0.1, "x^2")
+a_scale(Origin_x, Origin_y, Radius, 2)
+
+a_postamble
diff --git a/stltogl b/stltogl
new file mode 100644
index 0000000..5c75287
--- /dev/null
+++ b/stltogl
@@ -0,0 +1,21 @@
+"Get rid of most of the bumpf in the file
+:g/solid/d
+:g/loop/d
+:g/endfacet/d
+"Get rid of leading and trailing spaces
+:%s/^ *//g
+:%s/ *$//g
+"Unify spaces
+:%s/ \+/ /g
+"Rewrite each facet normal into a glNormal3f
+:%s/facet normal /glNormal3f(/g
+"Rewrite each vertex into a glVertex3f
+:%s/vertex /glVertex3f(/g
+"Rewrite line endings into function endings
+"and separate arguments
+:%s/$/);/g
+:%s/ /, /g
+"Colour each vertex
+:%s/\v(glV.*\n)(glV.*\n)(glV.*\n)/glColor3f(1.0, 0.0, 0.0); \1glColor3f(0.0, 1.0, 0.0); \2glColor3f(0.0, 0.0, 1.0); \3/g
+"Get rid of blank lines
+:g/^$/d
diff --git a/sudoku.rb b/sudoku.rb
new file mode 100644
index 0000000..9ac93c0
--- /dev/null
+++ b/sudoku.rb
@@ -0,0 +1,156 @@
+class Variable
+ def initialize(domain)
+ @domain = domain
+ end
+
+ def fixed?
+ if @domain.length == 1
+ @domain[0]
+ else
+ false
+ end
+ end
+
+ def include?(x)
+ @domain.include?(x)
+ end
+
+ def fix(x)
+ @domain = [x]
+ end
+
+ def eliminate(x)
+ @domain.delete(x) unless fixed?
+ end
+end
+
+def sudokuprint(grid, subsize, size = subsize * subsize)
+ w = Math.log10(size).ceil
+
+ bar = '┼ ┼' + (('─' * (subsize * (w + 1) + 1) + '┼') * subsize + ' ┼') * subsize
+
+ puts bar
+ puts bar
+ (0...size).step(subsize).each do |rs|
+ (rs...(rs + subsize)).each do |r|
+ (1..size).step(subsize).each do |ds|
+ print '│ │ '
+ (0...size).step(subsize).each do |b|
+ (b...(b + subsize)).each do |c|
+ (ds...(ds + subsize)).each do |d|
+ print (grid[r][c].include?(d) ? ("%#{w}d" % d) : (' ' * w)) + ' '
+ end
+ print '│ '
+ end
+ print '│ '
+ end
+ puts
+ end
+ puts bar
+ end
+ puts bar
+ end
+end
+
+SUBSIZE = 3
+SIZE = SUBSIZE * SUBSIZE
+
+boxes = []
+(0...SIZE).step(SUBSIZE).each do |r|
+ (0...SIZE).step(SUBSIZE).each do |c|
+ boxes.push([r, c])
+ end
+end
+
+grid = Array.new(SIZE) {
+ Array.new(SIZE) {
+ Variable.new((1..SIZE).to_a)
+ }
+}
+
+grid[0][3].fix(2)
+grid[0][4].fix(9)
+grid[0][5].fix(4)
+grid[0][6].fix(3)
+grid[0][8].fix(5)
+
+grid[1][0].fix(9)
+grid[1][1].fix(5)
+grid[1][3].fix(6)
+grid[1][5].fix(8)
+grid[1][6].fix(7)
+grid[1][8].fix(2)
+
+grid[2][2].fix(3)
+grid[2][7].fix(8)
+
+grid[3][1].fix(8)
+grid[3][8].fix(4)
+
+grid[4][1].fix(6)
+grid[4][3].fix(9)
+grid[4][5].fix(1)
+grid[4][7].fix(7)
+
+grid[5][0].fix(4)
+grid[5][7].fix(5)
+
+grid[6][1].fix(1)
+grid[6][6].fix(2)
+
+grid[7][0].fix(2)
+grid[7][2].fix(6)
+grid[7][3].fix(5)
+grid[7][5].fix(3)
+grid[7][7].fix(9)
+grid[7][8].fix(8)
+
+grid[8][0].fix(3)
+grid[8][2].fix(7)
+grid[8][3].fix(1)
+grid[8][4].fix(8)
+grid[8][5].fix(2)
+
+until grid.flatten.count(&:fixed?) == 81
+ (0...SIZE).each do |i|
+ row = grid[i]
+ row.each do |cell|
+ if v = cell.fixed?
+ row.each do |c2|
+ c2.eliminate(v)
+ end
+ end
+ end
+ end
+
+ (0...SIZE).each do |i|
+ col = grid.map { |row| row[i] }
+ col.each do |cell|
+ if v = cell.fixed?
+ col.each do |c2|
+ c2.eliminate(v)
+ end
+ end
+ end
+ end
+
+ (0...SIZE).each do |i|
+ rs, cs = boxes[i]
+ box = []
+ (rs...(rs + SUBSIZE)).each do |r|
+ (cs...(cs + SUBSIZE)).each do |c|
+ box.push(grid[r][c])
+ end
+ end
+
+ box.each do |cell|
+ if v = cell.fixed?
+ box.each do |c2|
+ c2.eliminate(v)
+ end
+ end
+ end
+ end
+end
+
+sudokuprint(grid, SUBSIZE, SIZE)
diff --git a/tabelvortoj.rb b/tabelvortoj.rb
new file mode 100644
index 0000000..2e85ccd
--- /dev/null
+++ b/tabelvortoj.rb
@@ -0,0 +1,101 @@
+#!/usr/bin/env ruby
+
+#!DESCRIBE: Flash cards for table words
+
+require "io/console"
+
+data = DATA.each_line.map { |l| l.strip.split(?;) }
+correct = 0
+total = 0
+
+loop do
+ get = data.sample(4)
+ if rand(2).zero?
+ qn = get.first.first
+ ans = get.first.last
+ opts = get.map(&:last).shuffle
+ else
+ qn = get.first.last
+ ans = get.first.first
+ opts = get.map(&:first).shuffle
+ end
+
+ print <<-EOT.chop
+
+#{qn}
+
+ 1: #{opts[0]}
+ 2: #{opts[1]}
+ 3: #{opts[2]}
+ 4: #{opts[3]}
+
+>:
+EOT
+
+ r = $stdin.getch.to_i
+
+ break unless r > 0 && r < 5
+
+ print "#{r} "
+ if ans == opts[r - 1]
+ puts ?✔
+ correct += 1
+ else
+ puts ?✘
+ end
+
+ total += 1
+end
+
+puts <<-EOT
+
+
+Score: #{correct}/#{total}
+EOT
+
+__END__
+kio;what
+tio;that
+io;something
+ĉio;everything
+nenio;nothing
+kiu;who, which
+tiu;that person, that one
+iu;someone
+ĉiu;everyone, every
+neniu;no-one, none of them
+kiam;when
+tiam;then
+iam;some time, ever
+ĉiam;always, every time
+neniam;never, no time
+kia;what kind of
+tia;that kind of
+ia;some kind of
+ĉia;every kind of
+nenia;no kind of
+kie;where
+tie;there
+ie;somewhere
+ĉie;everywhere
+nenie;nowhere
+kiel;how
+tiel;like that, thus
+iel;in some way
+ĉiel;in every way
+neniel;in no way
+kiom;how much
+tiom;that much
+iom;to some extent, a certain amount
+ĉiom;all of it, the whole amount
+neniom;none of it, no amount
+kial;why
+tial;for that reason
+ial;for some reason
+ĉial;for every reason
+nenial;for no reason
+kies;whose
+ties;their, that one's
+ies;someone's
+ĉies;everyone's
+nenies;no-one's
diff --git a/todo.vim b/todo.vim
new file mode 100644
index 0000000..96dc38f
--- /dev/null
+++ b/todo.vim
@@ -0,0 +1,6 @@
+"In normal mode, [ positions the cursor in the checkbox and waits for a character with which to replace the checkmark.
+au FileType todo nnoremap [ 0t]r
+"In insert mode, [ actually inserts a checkbox and a space.
+au FileType todo inoremap [ [<Space>]<Space>
+"Any file with a name ending in todo is a todo file.
+au BufRead,BufNewFile *todo set ft=todo