A pretend 'app' I made to give the kids clues for their Easter egg hunt. Each sample is a short sound recording of something in the house.
]]>A chart showing the status of our FoxESS solar panel and battery system. Needs an API token and device ID to work.
]]>A display screen for a Disability Interest Group conference stand.
]]>A tool to make sets of dominoes with words on. My mum asked me to make a set of dominoes with words in French and English, which prompted a lot of maths thinking about whether you could make it work the same way as a standard set of dominoes.
]]>An attempt to draw the graph of natural numbers, where each number is linked to the result of dividing by its greatest prime power factor.
]]>How quickly can you get to the target number by building up from 1, using only arithmetic operations?
]]>How should we mark (multi-)sets of objects given by the student?
]]>From https://github.com/samhenrigold/emoji-metadata
Search for emoji
]]>A big matching pairs game with 2026 cards.
]]>A clock for Christmas
]]>Each character's size is proportional to its ASCII code.
]]>A pure CSS copy of the new Network Rail clock design.
]]>Try your best to cover a unit circle with triangles and it'll show you how close you got to π.
]]>Information about the Beach Spectres project.
]]>A 5 minute timer for talks at Big MathsJam.
]]>A shader which shows points 'close' to a function. Made as a proof of concept to show that a shader can be much faster than plain JavaScript.
]]>Rotates the hues in the webcam image to help me see differences between colours.
]]>A tool to practise laying out the spectre tiling on a beach.
]]>Shows how to construct the Herschel enneahedron by truncating the middle of a triangular bipyramid.
]]>Guidance and case studies on running academic events that are accessible and inclusive.
]]>Tools to find images which describe how many black pixels they contain. Made for Matt Parker to accompany a video.
]]>A framework for easily creating beautiful presentations using HTML. Check out the live demo.
reveal.js comes with a broad range of features including nested slides, Markdown contents, PDF export, speaker notes and a JavaScript API. There's also a fully featured visual editor and platform for sharing reveal.js presentations at slides.com.
Presentations are written using HTML or Markdown but there's also an online editor for those of you who prefer a graphical interface. Give it a try at https://slides.com.
Here's a barebones example of a fully working reveal.js presentation: ```html
```
The presentation markup hierarchy needs to be .reveal > .slides > section where the section represents one slide and can be repeated indefinitely. If you place multiple section elements inside of another section they will be shown as vertical slides. The first of the vertical slides is the "root" of the others (at the top), and will be included in the horizontal sequence. For example:
```html
```
It's possible to write your slides using Markdown. To enable Markdown, add the data-markdown attribute to your <section> elements and wrap the contents in a <textarea data-template> like the example below. You'll also need to add the plugin/markdown/marked.js and plugin/markdown/markdown.js scripts (in that order) to your HTML file.
This is based on data-markdown from Paul Irish modified to use marked to support GitHub Flavored Markdown. Sensitive to indentation (avoid mixing tabs and spaces) and line breaks (avoid consecutive breaks).
```html
```
You can write your content as a separate file and have reveal.js load it at runtime. Note the separator arguments which determine how slides are delimited in the external file: the data-separator attribute defines a regular expression for horizontal slides (defaults to ^\r?\n---\r?\n$, a newline-bounded horizontal rule) and data-separator-vertical defines vertical slides (disabled by default). The data-separator-notes attribute is a regular expression for specifying the beginning of the current slide's speaker notes (defaults to notes?:, so it will match both "note:" and "notes:"). The data-charset attribute is optional and specifies which charset to use when loading the external file.
When used locally, this feature requires that reveal.js runs from a local web server. The following example customises all available options:
```html
```
Special syntax (through HTML comments) is available for adding attributes to Markdown elements. This is useful for fragments, amongst other things.
```html
```
Special syntax (through HTML comments) is available for adding attributes to the slide <section> elements generated by your Markdown.
```html
```
We use marked to parse Markdown. To customise marked's rendering, you can pass in options when configuring Reveal:
javascript
Reveal.initialize({
// Options which are passed into marked
// See https://github.com/chjj/marked#options-1
markdown: {
smartypants: true
}
});
At the end of your page you need to initialize reveal by running the following code. Note that all configuration values are optional and will default to the values specified below.
```javascript Reveal.initialize({
// Display presentation control arrows
controls: true,
// Help the user learn the controls by providing hints, for example by
// bouncing the down arrow when they first encounter a vertical slide
controlsTutorial: true,
// Determines where controls appear, "edges" or "bottom-right"
controlsLayout: 'bottom-right',
// Visibility rule for backwards navigation arrows; "faded", "hidden"
// or "visible"
controlsBackArrows: 'faded',
// Display a presentation progress bar
progress: true,
// Display the page number of the current slide
slideNumber: false,
// Push each slide change to the browser history
history: false,
// Enable keyboard shortcuts for navigation
keyboard: true,
// Enable the slide overview mode
overview: true,
// Vertical centering of slides
center: true,
// Enables touch navigation on devices with touch input
touch: true,
// Loop the presentation
loop: false,
// Change the presentation direction to be RTL
rtl: false,
// Randomizes the order of slides each time the presentation loads
shuffle: false,
// Turns fragments on and off globally
fragments: true,
// Flags whether to include the current fragment in the URL,
// so that reloading brings you to the same fragment position
fragmentInURL: false,
// Flags if the presentation is running in an embedded mode,
// i.e. contained within a limited portion of the screen
embedded: false,
// Flags if we should show a help overlay when the questionmark
// key is pressed
help: true,
// Flags if speaker notes should be visible to all viewers
showNotes: false,
// Global override for autoplaying embedded media (video/audio/iframe)
// - null: Media will only autoplay if data-autoplay is present
// - true: All media will autoplay, regardless of individual setting
// - false: No media will autoplay, regardless of individual setting
autoPlayMedia: null,
// Number of milliseconds between automatically proceeding to the
// next slide, disabled when set to 0, this value can be overwritten
// by using a data-autoslide attribute on your slides
autoSlide: 0,
// Stop auto-sliding after user input
autoSlideStoppable: true,
// Use this method for navigation when auto-sliding
autoSlideMethod: Reveal.navigateNext,
// Specify the average time in seconds that you think you will spend
// presenting each slide. This is used to show a pacing timer in the
// speaker view
defaultTiming: 120,
// Enable slide navigation via mouse wheel
mouseWheel: false,
// Hides the address bar on mobile devices
hideAddressBar: true,
// Opens links in an iframe preview overlay
// Add `data-preview-link` and `data-preview-link="false"` to customise each link
// individually
previewLinks: false,
// Transition style
transition: 'slide', // none/fade/slide/convex/concave/zoom
// Transition speed
transitionSpeed: 'default', // default/fast/slow
// Transition style for full page slide backgrounds
backgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom
// Number of slides away from the current that are visible
viewDistance: 3,
// Parallax background image
parallaxBackgroundImage: '', // e.g. "'https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg'"
// Parallax background size
parallaxBackgroundSize: '', // CSS syntax, e.g. "2100px 900px"
// Number of pixels to move the parallax background per slide
// - Calculated automatically unless specified
// - Set to 0 to disable movement along an axis
parallaxBackgroundHorizontal: null,
parallaxBackgroundVertical: null,
// The display mode that will be used to show slides
display: 'block'
}); ```
The configuration can be updated after initialization using the configure method:
```javascript // Turn autoSlide off Reveal.configure({ autoSlide: 0 });
// Start auto-sliding every 5s Reveal.configure({ autoSlide: 5000 }); ```
All presentations have a normal size, that is, the resolution at which they are authored. The framework will automatically scale presentations uniformly based on this size to ensure that everything fits on any given display or viewport.
See below for a list of configuration options related to sizing, including default values:
```javascript Reveal.initialize({
// ...
// The "normal" size of the presentation, aspect ratio will be preserved
// when the presentation is scaled to fit different resolutions. Can be
// specified using percentage units.
width: 960,
height: 700,
// Factor of the display size that should remain empty around the content
margin: 0.1,
// Bounds for smallest/largest possible scale to apply to content
minScale: 0.2,
maxScale: 1.5
}); ```
If you wish to disable this behavior and do your own scaling (e.g. using media queries), try these settings:
```javascript Reveal.initialize({
// ...
width: "100%",
height: "100%",
margin: 0,
minScale: 1,
maxScale: 1
}); ```
Reveal.js doesn't rely on any third party scripts to work but a few optional libraries are included by default. These libraries are loaded as dependencies in the order they appear, for example:
```javascript Reveal.initialize({ dependencies: [ // Cross-browser shim that fully implements classList - https://github.com/eligrey/classList.js/ { src: 'lib/js/classList.js', condition: function() { return !document.body.classList; } },
// Interpret Markdown in <section> elements
{ src: 'plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
// Syntax highlight for <code> elements
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
// Zoom in and out with Alt+click
{ src: 'plugin/zoom-js/zoom.js', async: true },
// Speaker notes
{ src: 'plugin/notes/notes.js', async: true },
// MathJax
{ src: 'plugin/math/math.js', async: true }
]
}); ```
You can add your own extensions using the same syntax. The following properties are available for each dependency object: - src: Path to the script to load - async: [optional] Flags if the script should load after reveal.js has started, defaults to false - callback: [optional] Function to execute when the script has loaded - condition: [optional] Function which must return true for the script to be loaded
To load these dependencies, reveal.js requires head.js (a script loading library) to be loaded before reveal.js.
A ready event is fired when reveal.js has loaded all non-async dependencies and is ready to start navigating. To check if reveal.js is already 'ready' you can call Reveal.isReady().
javascript
Reveal.addEventListener( 'ready', function( event ) {
// event.currentSlide, event.indexh, event.indexv
} );
Note that we also add a .ready class to the .reveal element so that you can hook into this with CSS.
Presentations can be configured to progress through slides automatically, without any user input. To enable this you will need to tell the framework how many milliseconds it should wait between slides:
javascript
// Slide every five seconds
Reveal.configure({
autoSlide: 5000
});
When this is turned on a control element will appear that enables users to pause and resume auto-sliding. Alternatively, sliding can be paused or resumed by pressing »A« on the keyboard. Sliding is paused automatically as soon as the user starts navigating. You can disable these controls by specifying autoSlideStoppable: false in your reveal.js config.
You can also override the slide duration for individual slides and fragments by using the data-autoslide attribute:
```html
After 2 seconds the first fragment will be shown.
After 10 seconds the next fragment will be shown.
Now, the fragment is displayed for 2 seconds before the next slide is shown.
```
To override the method used for navigation when auto-sliding, you can specify the autoSlideMethod setting. To only navigate along the top layer and ignore vertical slides, set this to Reveal.navigateRight.
Whenever the auto-slide mode is resumed or paused the autoslideresumed and autoslidepaused events are fired.
If you're unhappy with any of the default keyboard bindings you can override them using the keyboard config option:
javascript
Reveal.configure({
keyboard: {
13: 'next', // go to the next slide when the ENTER key is pressed
27: function() {}, // do something custom when ESC is pressed
32: null // don't do anything when SPACE is pressed (i.e. disable a reveal.js default binding)
}
});
You can swipe to navigate through a presentation on any touch-enabled device. Horizontal swipes change between horizontal slides, vertical swipes change between vertical slides. If you wish to disable this you can set the touch config option to false when initializing reveal.js.
If there's some part of your content that needs to remain accessible to touch events you'll need to highlight this by adding a data-prevent-swipe attribute to the element. One common example where this is useful is elements that need to be scrolled.
When working on presentation with a lot of media or iframe content it's important to load lazily. Lazy loading means that reveal.js will only load content for the few slides nearest to the current slide. The number of slides that are preloaded is determined by the viewDistance configuration option.
To enable lazy loading all you need to do is change your src attributes to data-src as shown below. This is supported for image, video, audio and iframe elements. Lazy loaded iframes will also unload when the containing slide is no longer visible.
```html
```
The Reveal object exposes a JavaScript API for controlling navigation and reading state:
```javascript // Navigation Reveal.slide( indexh, indexv, indexf ); Reveal.left(); Reveal.right(); Reveal.up(); Reveal.down(); Reveal.prev(); Reveal.next(); Reveal.prevFragment(); Reveal.nextFragment();
// Randomize the order of slides Reveal.shuffle();
// Toggle presentation states, optionally pass true/false to force on/off Reveal.toggleOverview(); Reveal.togglePause(); Reveal.toggleAutoSlide();
// Shows a help overlay with keyboard shortcuts, optionally pass true/false // to force on/off Reveal.toggleHelp();
// Change a config value at runtime Reveal.configure({ controls: true });
// Returns the present configuration options Reveal.getConfig();
// Fetch the current scale of the presentation Reveal.getScale();
// Retrieves the previous and current slide elements Reveal.getPreviousSlide(); Reveal.getCurrentSlide();
Reveal.getIndices(); // { h: 0, v: 0, f: 0 } Reveal.getSlidePastCount(); Reveal.getProgress(); // (0 == first slide, 1 == last slide) Reveal.getSlides(); // Array of all slides Reveal.getTotalSlides(); // Total number of slides
// Returns the speaker notes for the current slide Reveal.getSlideNotes();
// State checks Reveal.isFirstSlide(); Reveal.isLastSlide(); Reveal.isOverview(); Reveal.isPaused(); Reveal.isAutoSliding(); ```
Custom key bindings can be added and removed using the following Javascript API. Custom key bindings will override the default keyboard bindings, but will in turn be overridden by the user defined bindings in the keyboard config option.
javascript
Reveal.addKeyBinding( binding, callback );
Reveal.removeKeyBinding( keyCode );
For example
```javascript // The binding parameter provides the following properties // keyCode: the keycode for binding to the callback // key: the key label to show in the help overlay // description: the description of the action to show in the help overlay Reveal.addKeyBinding( { keyCode: 84, key: 'T', description: 'Start timer' }, function() { // start timer } )
// The binding parameter can also be a direct keycode without providing the help description Reveal.addKeyBinding( 82, function() { // reset timer } ) ```
This allows plugins to add key bindings directly to Reveal so they can
A slidechanged event is fired each time the slide is changed (regardless of state). The event object holds the index values of the current slide as well as a reference to the previous and current slide HTML nodes.
Some libraries, like MathJax (see #226), get confused by the transforms and display states of slides. Often times, this can be fixed by calling their update or render function from this callback.
javascript
Reveal.addEventListener( 'slidechanged', function( event ) {
// event.previousSlide, event.currentSlide, event.indexh, event.indexv
} );
The presentation's current state can be fetched by using the getState method. A state object contains all of the information required to put the presentation back as it was when getState was first called. Sort of like a snapshot. It's a simple object that can easily be stringified and persisted or sent over the wire.
```javascript Reveal.slide( 1 ); // we're on slide 1
var state = Reveal.getState();
Reveal.slide( 3 ); // we're on slide 3
Reveal.setState( state ); // we're back on slide 1 ```
If you set data-state="somestate" on a slide <section>, "somestate" will be applied as a class on the document element when that slide is opened. This allows you to apply broad style changes to the page based on the active slide.
Furthermore you can also listen to these changes in state via JavaScript:
javascript
Reveal.addEventListener( 'somestate', function() {
// TODO: Sprinkle magic
}, false );
Slides are contained within a limited portion of the screen by default to allow them to fit any display and scale uniformly. You can apply full page backgrounds outside of the slide area by adding a data-background attribute to your <section> elements. Four different types of backgrounds are supported: color, image, video and iframe.
All CSS color formats are supported, including hex values, keywords, rgba() or hsl().
```html
```
By default, background images are resized to cover the full page. Available options:
| Attribute | Default | Description | | :------------------------------- | :--------- | :---------- | | data-background-image | | URL of the image to show. GIFs restart when the slide opens. | | data-background-size | cover | See background-size on MDN. | | data-background-position | center | See background-position on MDN. | | data-background-repeat | no-repeat | See background-repeat on MDN. | | data-background-opacity | 1 | Opacity of the background image on a 0-1 scale. 0 is transparent and 1 is fully opaque. |
```html
```
Automatically plays a full size video behind the slide.
| Attribute | Default | Description |
| :--------------------------- | :------ | :---------- |
| data-background-video | | A single video source, or a comma separated list of video sources. |
| data-background-video-loop | false | Flags if the video should play repeatedly. |
| data-background-video-muted | false | Flags if the audio should be muted. |
| data-background-size | cover | Use cover for full screen and some cropping or contain for letterboxing. |
| data-background-opacity | 1 | Opacity of the background video on a 0-1 scale. 0 is transparent and 1 is fully opaque. |
```html
```
Embeds a web page as a slide background that covers 100% of the reveal.js width and height. The iframe is in the background layer, behind your slides, and as such it's not possible to interact with it by default. To make your background interactive, you can add the data-background-interactive attribute.
```html
```
Backgrounds transition using a fade animation by default. This can be changed to a linear sliding transition by passing backgroundTransition: 'slide' to the Reveal.initialize() call. Alternatively you can set data-background-transition on any section with a background to override that specific transition.
If you want to use a parallax scrolling background, set the first two properties below when initializing reveal.js (the other two are optional).
```javascript Reveal.initialize({
// Parallax background image
parallaxBackgroundImage: '', // e.g. "https://s3.amazonaws.com/hakim-static/reveal-js/reveal-parallax-1.jpg"
// Parallax background size
parallaxBackgroundSize: '', // CSS syntax, e.g. "2100px 900px" - currently only pixels are supported (don't use % or auto)
// Number of pixels to move the parallax background per slide
// - Calculated automatically unless specified
// - Set to 0 to disable movement along an axis
parallaxBackgroundHorizontal: 200,
parallaxBackgroundVertical: 50
}); ```
Make sure that the background size is much bigger than screen size to allow for some scrolling. View example.
The global presentation transition is set using the transition config value. You can override the global transition for a specific slide by using the data-transition attribute:
```html
```
You can also use different in and out transitions for the same slide:
```html
``
You can choose fromnone,fade,slide,convex,concaveandzoom`.
It's easy to link between slides. The first example below targets the index of another slide whereas the second targets a slide with an ID attribute (<section id="some-slide">):
html
<a href="#/2/2">Link</a>
<a href="#/some-slide">Link</a>
You can also add relative navigation links, similar to the built in reveal.js controls, by appending one of the following classes on any element. Note that each element is automatically given an enabled class when it's a valid navigation route based on the current slide.
html
<a href="#" class="navigate-left">
<a href="#" class="navigate-right">
<a href="#" class="navigate-up">
<a href="#" class="navigate-down">
<a href="#" class="navigate-prev"> <!-- Previous vertical or horizontal slide -->
<a href="#" class="navigate-next"> <!-- Next vertical or horizontal slide -->
Fragments are used to highlight individual elements on a slide. Every element with the class fragment will be stepped through before moving on to the next slide. Here's an example: http://revealjs.com/#/fragments
The default fragment style is to start out invisible and fade in. This style can be changed by appending a different class to the fragment:
```html
grow
shrink
fade-out
fade-up (also down, left and right!)
fades in, then out when we move to the next step
fades in, then obfuscate when we move to the next step
blue only once
highlight-red
highlight-green
highlight-blue
```
Multiple fragments can be applied to the same element sequentially by wrapping it, this will fade in the text on the first step and fade it back out on the second.
```html
```
The display order of fragments can be controlled using the data-fragment-index attribute.
```html
Appears last
Appears first
Appears second
```
When a slide fragment is either shown or hidden reveal.js will dispatch an event.
Some libraries, like MathJax (see #505), get confused by the initially hidden fragment elements. Often times this can be fixed by calling their update or render function from this callback.
javascript
Reveal.addEventListener( 'fragmentshown', function( event ) {
// event.fragment = the fragment DOM element
} );
Reveal.addEventListener( 'fragmenthidden', function( event ) {
// event.fragment = the fragment DOM element
} );
By default, Reveal is configured with highlight.js for code syntax highlighting. To enable syntax highlighting, you'll have to load the highlight plugin (plugin/highlight/highlight.js) and a highlight.js CSS theme (Reveal comes packaged with the zenburn theme: lib/css/zenburn.css).
javascript
Reveal.initialize({
// More info https://github.com/hakimel/reveal.js#dependencies
dependencies: [
{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
]
});
Below is an example with clojure code that will be syntax highlighted. When the data-trim attribute is present, surrounding whitespace is automatically removed. HTML will be escaped by default. To avoid this, for example if you are using <mark> to call out a line of code, add the data-noescape attribute to the <code> element.
```html
(def lazy-fib
(concat
[0 1]
((fn rfib [a b]
(lazy-cons (+ a b) (rfib b (+ a b)))) 0 1)))
```
If you would like to display the page number of the current slide you can do so using the slideNumber and showSlideNumber configuration values.
```javascript // Shows the slide number using default formatting Reveal.configure({ slideNumber: true });
// Slide number formatting can be configured using these variables: // "h.v": horizontal . vertical slide number (default) // "h/v": horizontal / vertical slide number // "c": flattened slide number // "c/t": flattened slide number / total slides Reveal.configure({ slideNumber: 'c/t' });
// Control which views the slide number displays on using the "showSlideNumber" value: // "all": show on all views (default) // "speaker": only show slide numbers on speaker notes view // "print": only show slide numbers when printing to PDF Reveal.configure({ showSlideNumber: 'speaker' }); ```
Press »ESC« or »O« keys to toggle the overview mode on and off. While you're in this mode, you can still navigate between slides, as if you were at 1,000 feet above your presentation. The overview mode comes with a few API hooks:
```javascript Reveal.addEventListener( 'overviewshown', function( event ) { / ... / } ); Reveal.addEventListener( 'overviewhidden', function( event ) { / ... / } );
// Toggle the overview mode programmatically Reveal.toggleOverview(); ```
Just press »F« on your keyboard to show your presentation in fullscreen mode. Press the »ESC« key to exit fullscreen mode.
Add data-autoplay to your media element if you want it to automatically start playing when the slide is shown:
```html
```
If you want to enable or disable autoplay globally, for all embedded media, you can use the autoPlayMedia configuration option. If you set this to true ALL media will autoplay regardless of individual data-autoplay attributes. If you initialize with autoPlayMedia: false NO media will autoplay.
Note that embedded HTML5 <video>/<audio> and YouTube/Vimeo iframes are automatically paused when you navigate away from a slide. This can be disabled by decorating your element with a data-ignore attribute.
reveal.js automatically pushes two post messages to embedded iframes. slide:start when the slide containing the iframe is made visible and slide:stop when it is hidden.
Sometimes it's desirable to have an element, like an image or video, stretch to consume as much space as possible within a given slide. This can be done by adding the .stretch class to an element as seen below:
```html
```
Limitations: - Only direct descendants of a slide section can be stretched - Only one descendant per slide section can be stretched
The framework has a built-in postMessage API that can be used when communicating with a presentation inside of another window. Here's an example showing how you'd make a reveal.js instance in the given window proceed to slide 2:
javascript
<window>.postMessage( JSON.stringify({ method: 'slide', args: [ 2 ] }), '*' );
When reveal.js runs inside of an iframe it can optionally bubble all of its events to the parent. Bubbled events are stringified JSON with three fields: namespace, eventName and state. Here's how you subscribe to them from the parent window:
javascript
window.addEventListener( 'message', function( event ) {
var data = JSON.parse( event.data );
if( data.namespace === 'reveal' && data.eventName ==='slidechanged' ) {
// Slide changed, see data.state for slide number
}
} );
This cross-window messaging can be toggled on or off using configuration flags.
```javascript Reveal.initialize({ // ...
// Exposes the reveal.js API through window.postMessage
postMessage: true,
// Dispatches all reveal.js events to the parent window through postMessage
postMessageEvents: false
}); ```
Presentations can be exported to PDF via a special print stylesheet. This feature requires that you use Google Chrome or Chromium and to be serving the presentation from a webserver. Here's an example of an exported presentation that's been uploaded to SlideShare: http://www.slideshare.net/hakimel/revealjs-300.
Fragments are printed on separate slides by default. Meaning if you have a slide with three fragment steps, it will generate three separate slides where the fragments appear incrementally.
If you prefer printing all fragments in their visible states on the same slide you can set the pdfSeparateFragments config option to false.
Export dimensions are inferred from the configured presentation size. Slides that are too tall to fit within a single page will expand onto multiple pages. You can limit how many pages a slide may expand onto using the pdfMaxPagesPerSlide config option, for example Reveal.configure({ pdfMaxPagesPerSlide: 1 }) ensures that no slide ever grows to more than one printed page.
To enable the PDF print capability in your presentation, the special print stylesheet at /css/print/pdf.css must be loaded. The default index.html file handles this for you when print-pdf is included in the query string. If you're using a different HTML template, you can add this to your HEAD:
```html
```
print-pdf included in the query string i.e. http://localhost:8000/?print-pdf. You can test this with revealjs.com?print-pdf.showNotes=true to the query string: http://localhost:8000/?print-pdf&showNotes=true
Alternatively you can use the decktape project.
The framework comes with a few different themes included:
Each theme is available as a separate stylesheet. To change theme you will need to replace black below with your desired theme name in index.html:
html
<link rel="stylesheet" href="proxy.php?url=https%3A%2F%2Fsomethingorotherwhatever.com%2Fcss%2Ftheme%2Fblack.css" id="theme">
If you want to add a theme of your own see the instructions here: /css/theme/README.md.
reveal.js comes with a speaker notes plugin which can be used to present per-slide notes in a separate browser window. The notes window also gives you a preview of the next upcoming slide so it may be helpful even if you haven't written any notes. Press the »S« key on your keyboard to open the notes window.
A speaker timer starts as soon as the speaker view is opened. You can reset it to 00:00:00 at any time by simply clicking/tapping on it.
Notes are defined by appending an <aside> element to a slide as seen below. You can add the data-markdown attribute to the aside element if you prefer writing notes using Markdown.
Alternatively you can add your notes in a data-notes attribute on the slide. Like <section data-notes="Something important"></section>.
When used locally, this feature requires that reveal.js runs from a local web server.
```html
```
If you're using the external Markdown plugin, you can add notes with the help of a special delimiter:
```html
Here is some content...
Note: This will only display in the notes window. ```
Notes are only visible to the speaker inside of the speaker view. If you wish to share your notes with others you can initialize reveal.js with the showNotes configuration value set to true. Notes will appear along the bottom of the presentations.
When showNotes is enabled notes are also included when you export to PDF. By default, notes are printed in a semi-transparent box on top of the slide. If you'd rather print them on a separate page after the slide, set showNotes: "separate-page".
The speaker notes window will also show:
The pacing timer can be enabled by configuring by the defaultTiming parameter in the Reveal configuration block, which specifies the number of seconds per slide. 120 can be a reasonable rule of thumb. Timings can also be given per slide <section> by setting the data-timing attribute. Both values are in numbers of seconds.
In some cases it can be desirable to run notes on a separate device from the one you're presenting on. The Node.js-based notes plugin lets you do this using the same note definitions as its client side counterpart. Include the required scripts by adding the following dependencies:
```javascript Reveal.initialize({ // ...
dependencies: [
{ src: 'socket.io/socket.io.js', async: true },
{ src: 'plugin/notes-server/client.js', async: true }
]
}); ```
Then:
npm installnode plugin/notes-serverThe multiplex plugin allows your audience to view the slides of the presentation you are controlling on their own phone, tablet or laptop. As the master presentation navigates the slides, all client presentations will update in real time. See a demo at https://reveal-js-multiplex-ccjbegmaii.now.sh/.
The multiplex plugin needs the following 3 things to operate:
Served from a static file server accessible (preferably) only to the presenter. This need only be on your (the presenter's) computer. (It's safer to run the master presentation from your own computer, so if the venue's Internet goes down it doesn't stop the show.) An example would be to execute the following commands in the directory of your master presentation:
npm install node-staticstaticIf you want to use the speaker notes plugin with your master presentation then make sure you have the speaker notes plugin configured correctly along with the configuration shown below, then execute node plugin/notes-server in the directory of your master presentation. The configuration below will cause it to connect to the socket.io server as a master, as well as launch your speaker-notes/static-file server.
You can then access your master presentation at http://localhost:1947
Example configuration:
```javascript Reveal.initialize({ // other options...
multiplex: {
// Example values. To generate your own, see the socket.io server instructions.
secret: '13652805320794272084', // Obtained from the socket.io server. Gives this (the master) control of the presentation
id: '1ea875674b17ca76', // Obtained from socket.io server
url: 'https://reveal-js-multiplex-ccjbegmaii.now.sh' // Location of socket.io server
},
// Don't forget to add the dependencies
dependencies: [
{ src: '//cdn.socket.io/socket.io-1.3.5.js', async: true },
{ src: 'plugin/multiplex/master.js', async: true },
// and if you want speaker notes
{ src: 'plugin/notes-server/client.js', async: true }
// other dependencies...
]
}); ```
Served from a publicly accessible static file server. Examples include: GitHub Pages, Amazon S3, Dreamhost, Akamai, etc. The more reliable, the better. Your audience can then access the client presentation via http://example.com/path/to/presentation/client/index.html, with the configuration below causing them to connect to the socket.io server as clients.
Example configuration:
```javascript Reveal.initialize({ // other options...
multiplex: {
// Example values. To generate your own, see the socket.io server instructions.
secret: null, // null so the clients do not have control of the master presentation
id: '1ea875674b17ca76', // id, obtained from socket.io server
url: 'https://reveal-js-multiplex-ccjbegmaii.now.sh' // Location of socket.io server
},
// Don't forget to add the dependencies
dependencies: [
{ src: '//cdn.socket.io/socket.io-1.3.5.js', async: true },
{ src: 'plugin/multiplex/client.js', async: true }
// other dependencies...
]
}); ```
Server that receives the slideChanged events from the master presentation and broadcasts them out to the connected client presentations. This needs to be publicly accessible. You can run your own socket.io server with the commands:
npm installnode plugin/multiplexOr you can use the socket.io server at https://reveal-js-multiplex-ccjbegmaii.now.sh/.
You'll need to generate a unique secret and token pair for your master and client presentations. To do so, visit http://example.com/token, where http://example.com is the location of your socket.io server. Or if you're going to use the socket.io server at https://reveal-js-multiplex-ccjbegmaii.now.sh/, visit https://reveal-js-multiplex-ccjbegmaii.now.sh/token.
You are very welcome to point your presentations at the Socket.io server running at https://reveal-js-multiplex-ccjbegmaii.now.sh/, but availability and stability are not guaranteed.
For anything mission critical I recommend you run your own server. The easiest way to do this is by installing now. With that installed, deploying your own Multiplex server is as easy running the following command from the reveal.js folder: now plugin/multiplex.
The socket.io server can play the role of static file server for your client presentation, as in the example at https://reveal-js-multiplex-ccjbegmaii.now.sh/. (Open https://reveal-js-multiplex-ccjbegmaii.now.sh/ in two browsers. Navigate through the slides on one, and the other will update to match.)
Example configuration:
```javascript Reveal.initialize({ // other options...
multiplex: {
// Example values. To generate your own, see the socket.io server instructions.
secret: null, // null so the clients do not have control of the master presentation
id: '1ea875674b17ca76', // id, obtained from socket.io server
url: 'example.com:80' // Location of your socket.io server
},
// Don't forget to add the dependencies
dependencies: [
{ src: '//cdn.socket.io/socket.io-1.3.5.js', async: true },
{ src: 'plugin/multiplex/client.js', async: true }
// other dependencies...
]
```
It can also play the role of static file server for your master presentation and client presentations at the same time (as long as you don't want to use speaker notes). (Open https://reveal-js-multiplex-ccjbegmaii.now.sh/ in two browsers. Navigate through the slides on one, and the other will update to match. Navigate through the slides on the second, and the first will update to match.) This is probably not desirable, because you don't want your audience to mess with your slides while you're presenting. ;)
Example configuration:
```javascript Reveal.initialize({ // other options...
multiplex: {
// Example values. To generate your own, see the socket.io server instructions.
secret: '13652805320794272084', // Obtained from the socket.io server. Gives this (the master) control of the presentation
id: '1ea875674b17ca76', // Obtained from socket.io server
url: 'example.com:80' // Location of your socket.io server
},
// Don't forget to add the dependencies
dependencies: [
{ src: '//cdn.socket.io/socket.io-1.3.5.js', async: true },
{ src: 'plugin/multiplex/master.js', async: true },
{ src: 'plugin/multiplex/client.js', async: true }
// other dependencies...
]
}); ```
If you want to display math equations in your presentation you can easily do so by including this plugin. The plugin is a very thin wrapper around the MathJax library. To use it you'll need to include it as a reveal.js dependency, find our more about dependencies here.
The plugin defaults to using LaTeX but that can be adjusted through the math configuration object. Note that MathJax is loaded from a remote server. If you want to use it offline you'll need to download a copy of the library and adjust the mathjax configuration value.
Below is an example of how the plugin can be configured. If you don't intend to change these values you do not need to include the math config object at all.
```js Reveal.initialize({ // other options ...
math: {
mathjax: 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js',
config: 'TeX-AMS_HTML-full' // See http://docs.mathjax.org/en/latest/config-files.html
},
dependencies: [
{ src: 'plugin/math/math.js', async: true }
]
}); ```
Read MathJax's documentation if you need HTTPS delivery or serving of specific versions for stability.
The basic setup is for authoring presentations only. The full setup gives you access to all reveal.js features and plugins such as speaker notes as well as the development tasks needed to make changes to the source.
The core of reveal.js is very easy to install. You'll simply need to download a copy of this repository and open the index.html file directly in your browser.
Some reveal.js features, like external Markdown and speaker notes, require that presentations run from a local web server. The following instructions will set up such a server as well as all of the development tasks needed to make edits to the reveal.js source code.
Install Node.js (4.0.0 or later)
Clone the reveal.js repository
sh
$ git clone https://github.com/hakimel/reveal.js.git
Navigate to the reveal.js folder
sh
$ cd reveal.js
Install dependencies
sh
$ npm install
Serve the presentation and monitor source files for changes
sh
$ npm start
Open http://localhost:8000 to view your presentation
You can change the port by using npm start -- --port=8001.
MIT licensed
Copyright (C) 2018 Hakim El Hattab, http://hakim.se
]]>Apply the Julia transform to the image from your webcam. It's horrible!
]]>Shows how the Mandelbrot set relates to the Julia set. Made for Matt Parker.
]]>Shows the Mandelbrot and Julia sets side-by-side, so you can see how they relate to each other.
]]>Irrational Collatz
Davide Rotondo asked SeqFan what happens if you apply the Collatz rule to the integer part of irrational numbers?
This plots the number of steps to get back to 1 for values in the chosen range.
]]>I told Brady Haran about the Herschel enneahedron.
]]>What if the factorisation of an integer was presented like the credits at the end of a movie?
]]>A game discussed on SeqFans on 2025-05-21, attributed to Konstantin Knop:
You have an infinite row of cells numbered with the integers (open in both directions). In the starting position, you have N counters on cell 0. At each move, you pick a starting cell, and move K counters from that starting cell to the cell K steps either to the right or left. The object of the game is to finish with all N counters on cell 1 in the smallest possible number of moves.
]]>Knight's tour explorer
Made by Christian Lawson-Perfect in 2025 - https://somethingorotherwhatever.com
This is an Elm app. To rebuild this, use https://elm-lang.org/. The build command is elm make src/App.elm --output=app.js
The necessary files for use are:
A thing to help me lay out the Spectre aperiodic monotile tiling.
]]>A page that renders your chosen number in as many numeral systems as it can.
]]>An interactive 3D rendering of the Herschel enneahedron
]]>A tool which shows roughly how a given image will look with different kinds of colour vision deficiency.
]]>A poster presented at Newcastle University's learning and teaching conference.
]]>A clock where the 'hands' follow the Hilbert curve
]]>A tiny tool to convert between denominations of Roman coins.
]]>This document was made by Scarlett Spackman and Christian Lawson-Perfect as part of a Summer project in 2024.
Scarlett is an undergraduate MMath student leaving Stage 3.
Christian is a senior learning software developer in the digital learning unit.
The purpose of this page is to show the importance of implementing reasonable adjustments in response to student needs and to offer you practical guidance on how to implement these adjustments efficiently. We argue that adopting a more inclusive teaching style using universal design principles is a good place to start and that embracing these changes will foster a supportive and fair learning environment that empowers each student, disabled or not, to reach their maximum potential.
This is a Chirun package.
This work © 2024 by Scarlett Spackman and Christian Lawson-Perfect for Newcastle University is licensed under CC BY 4.0
.
Made for Matt Parker. A visualisation of the Thomson problem - distributing points on a sphere to minimise the repulsion energy.
]]>A few variations on sorting things in space. I made this for Helen but she didn't like it.
]]>Material made to support an event I ran at Newcastle University.
]]>A page to browse through all the fonts available on fontsource.com, and to fiddle with the variable axes.
]]>A map just for me. I realised that there were things I wanted to remember about certain places, but generic map apps show too much other information. I can make markers for new places and add relevant links or text.
]]>Animation of dots moving along a Hilbert curve.
]]>The idea was to make a puzzle out of directing tokens along a train track diagram. I got as far as trying it with arithmetic operations and didn't really like it. Something with generic symbols and some restriction on the order they go through vertices might work better, but would be work.
]]>A table showing when the monthly MathsJams are, for each month of the year.
]]>An animation showing how you can cover the plane using only one of each n-gon.
]]>From https://github.com/samhenrigold/emoji-metadata
Search for emoji
]]>I thought it'd be nice to make a game out of connecting different places with roads and building things. I couldn't decide how much resource management was fun.
]]>A tool to design a an all-over printed hoodie using the Spectre aperiodic monotile.
]]>A pattern to print out on a big vinyl sticker, to use as a whiteboard in my office.
]]>Recreating a design I saw made by someone else, but on a hex grid.
]]>A Mandelbrot fractal visualiser written using WebGL.
]]>Made to accompany a video about an integer sequence I came up with.
]]>A tool I made to help think about how to make a customisable, accessible colour palette.
]]>A tool which plots a list of colours on polar coordinates, to help spot similar colours.
]]>An animation of text diffusing.
]]>Another iteration in my ongoing exploration of the Unicode character set.
]]>A tool to plot the votes for each Big Internet Math-Off match, over time
]]>Shows how to add a TeX comand to MathJax 3, which parses its (non-TeX) input and returns TeX code to go be substituted for the macro.
Tooted: https://mathstodon.xyz/@christianp/112631902685965806
]]>I've been wondering for a while how to put arbitrary HTML inside an Elm app.
I wondered if I could register a custom HTML element which takes a string of HTML code as an attribute, and sets the innerHTML of its shadow root to that code. The Elm app could then create an instance of that element and pass a string of HTML to it.
It works!
]]>A poster about Numbas, an open-source e-assessment system.
It's designed to be printed at A2 size.
The file numbas-poster.svg is the source file; the PDF and PNG files are created from it.
This work is licensed under CC BY 4.0. You are free to share and adapt this poster under the terms of the CC BY 4.0 licence.

You'll be shown five numbers, between 100 and 999. Try to guess what order the numbers will go in, from smallest to biggest.
]]>An editor for Eukleides diagrams.
]]>Shows how to embed a Numbas question in a page using an iframe tag
]]>A calculator which takes JME expressions.
]]>A simple spinner animation using the SVG <animate> tag.
Trying to replicate Elliot's thing to nudge a point along a curve.
]]>A clock with a hand for each power of two seconds.
]]>Letters are positioned on a binary tree. How quickly can you visit every letter?
]]>I wanted to make a game where you have to think a bit, but not too much.
]]>A button that takes a number and makes it more complicated by expanding it to a mathematical operation.
]]>JSON files:
lines.json Maps line names ("GREEN" or "YELLOW") to a list of stations along that line, with properties "key" (string) and "position" (list of two floats).
platforms.json From https://github.com/danielgjackson/metro-rti/blob/main/data/platforms.json Maps station keys to objects:
"platformNumber": int "direction": string, "IN" or "OUT" "helperText": string, readable description of the direction of travel
station-directions.json Maps station keys to a dictionary mapping platform numbers to the angle of that platform on the map.
station-positions.json Maps line names ("green" or "yellow") to a list of pairs of floats, giving the position on the map of each station on the line.
stations.json From https://github.com/danielgjackson/metro-rti/blob/main/data/stations.json Maps station keys to readable station names.
]]>A game invented by Alexandre Muñiz: https://mathstodon.xyz/@two_star/112242224494626411
]]>Here's a puzzle game. I call it Reverse the List of Integers. How it works is, you start with a list of positive integers, (e.g. [7, 5, 3]) and your goal is to make the same list, in reverse ([3, 5, 7]). You have two moves you can make:
1) Split an integer into two smaller integers. (e.g. [7, 5, 3] → [6, 1, 5, 3]) 2) Combine (add) two integers into a larger one. (e.g. reverse the last e.g.)
There are two restrictions that seem natural for making this into an interesting game:
1) You can never make an integer greater than the largest integer in the original list. 2) You can never make a move that results in the same integer appearing in the list more than once.
A clock which shows the unix epoch - the number of seconds since new year, 1970. There is a hand for each power of 10 seconds.
]]>An implementation of Quanta Magazine's Hyperjumps game. There was some discussion on the Talking Maths in Public group about how difficult to understand Quanta's presentation is.
]]>I'd like to colour the days of the year by changing the hue parameter of an oklch colour with the day of the year. How do I have to shift the number so that summer is reddish and winter is blueish?
]]>Christian Lawson-Perfect (https://somethingorotherwhatever.com, [email protected]), 2024, based on the original by Matthew T. Smith for Pegasus Development, 1994.
Tiny Elvis was a Windows 3.1 program written by Matthew T. Smith. It placed a tiny pixel-art Elvis on your desktop, who would occasionally comment on the enormity of the things around him.
I've recreated Tiny Elvis as a web page, using the original art and sounds. The README says the program is in the public domain, but asks us to only distribute the original files without disassembling the executable. There's a vague bit saying it's OK to extract the icons.
I think that extracting the picture files 30 years later is probably OK,
but to be safe the git repository only includes the files from the original
zip package, along with a Makefile to extract the art and convert it to png
format, using wrestool and imagemagick's convert command.
I didn't do anything with the Easter Egg messages which you could activate in the original by double-clicking the E icon in the "About Tiny Elvis" dialog.
To recreate the files after cloning the repository, run make extract_icons
and then make default resources.
A tool to help me, with limited colour vision, make accessible colour palettes.
]]>Experimenting with how the curves joining points in a permutation chart are chosen.
]]>This is a puzzle invented by Ed Kirkby, and shown to me at MathsJam Gathering in 2022.
There's a blog post about it at https://aperiodical.com/2023/12/the-double-back-puzzle-is-a-nice-mix-of-towers-of-hanoi-and-solitaire/.
]]>A tool to make art consisting of text drawn over an image, avoiding a mask drawn by hand. Heavily inspired by Litographs, made for Christmas cards in 2023.
]]>The beginnings of an idea about quickly cutting out sprites from a drawing. I used a photo of myself as a toddler as the test image.
]]>A Morse code key made for Ayliean MacDonald.
]]>A web component to embed a GeoGebra applet.
It's a wrapper around deployggb.js provided by GeoGebra.
Write GeoGebra commands inside the tag. These commands are evaluated immediately after the applet loads.
<geogebra-applet>
A = (1,2)
</geogebra-applet>
<geogebra-applet material="sA5Mb4vd" />
<geogebra-applet material="https://www.geogebra.org/m/sA5Mb4vd" />
You can give just the ID, or you can give the full URL of a geogebra material, because that's easier to think about!
There's a full API reference and a page of examples.
]]>A really simple tool which generates a QR code containing the given text.
]]>For my toddler to bang away at the keyboard
]]>Unicode is the standard encoding for text. There are thousands of glyphs, representing letters, characters, symbols and marks from a huge variety of scripts and contexts.
There are many repeated, variant or combined characters. These can be normalized to a subset of characters, using standard normalization algorithms.
These algorithms are generic: when used in a mathematical context, they might not apply an equivalence between two characters, or omit some information that would be useful.
So this project aims to compile a dictionary of mappings from less-common Unicode characters to the symbols conventionally used in linear mathematical expressions.
The motivation is to support more characters in the JME language used by Numbas. The JME grammar has the following kinds of token:
Any string of letter characters is acceptable for name tokens, but there are many equivalences that should be applied:
α and alpha.∞ and infinity.x² is equivalent to x^2.√ is equivalent to sqrt, or to operations that are conventionally typed in ASCII, e.g. × is equivalent to *.Digit symbols should be normalized to the ASCII digits 0-9, where possible. Non-European scripts for representing numbers would need to be dealt with individually.
There are lots of varieties of brackets, which should normalize to the ASCII parentheses (), square brackets [] and curly brackets {}.
There is a Jupyter notebook, unicode-math-mapping.ipynb, which contains code for working through subsets of Unicode and producing mapping dictionaries.
There are some mappings that can be produced automatically, and some that had to be written out manually - these are defined by the .tsv files in the root of this repository.
The mapping information is stored in .json files in the final_data directory.
Each of these files contains a single dictionary mapping single Unicode characters to an equivalent string, and an array of annotations, which are themselves ASCII strings.
For example, this is the entry in final_data/symbols.json for 𝒚, "MATHEMATICAL BOLD ITALIC SMALL Y":
"\ud835\udc9a": [
"y",
[
"BOLD",
"ITALIC"
]
],
There are five files:
greek.json - mapping Greek letters to their English names, e.g. α to alpha.letters.json - mapping mathematical letters to their standard equivalents, with annotatoins.subscripts.json - mapping superscript characters to their standard equivalents.superscripts.json - mapping subscript characters to their standard equivalents.symbols.json - mapping all sorts of mathematical symbols to common symbols, names, or sub-expressions. Some symbols are mapped to a string of the form not NAME - you might have to do some processing to interpret these correctly, instead of just substituting the mapped string into the expression being parsed.The mappings must be applied as part of the tokenisation step when parsing a mathematical expression.
It is not correct to do a global substitution of characters before parsing: for example, in the expression α = "α", the second occurrence of α should be preserved because it's inside a string literal.
You will have to come up with a way of applying the produced mappings to a particular computer algebra system.
There were many decisions to make in producing the mapping of characters. I omitted most symbols relating to operations that are very unlikely to be used in an undergraduate maths course.
The function names for mappings were sometimes chosen arbitrarily - there might be standard names for these in some computer algebra systems.
If you use these mappings, please tell me!
]]>This repository contains code and vector image files to produce the aperiodic monotiles found by David Smith, Joseph Samuel Myers, Craig S. Kaplan, and Chaim Goodman-Strauss.
There are two monotiles, which each tile the plane aperiodically.
A 'hat':

And a 'turtle':

Each file produces a single copy of the tile. Several copies of the tile, with some flipped or mirrored, fit together to tile the plane.
The files are:
This is a Django project for serving ActivityPub actors, designed for bot accounts.
The aim is to provide just enough features to provide an account that other people can follow, and to manage followers and sending out new posts.
I'm sharing this code as-is, only because it doesn't cost me anything other than the time to write this README!
I hope it's useful, and you're very welcome to use it as a starting-point to build your own project.
If you're looking for a more extensible Python ActivityPub implementation, bovine is worth looking at first.
Assumptions:
You need Python 3.9 or later to run this.
Clone this repository (I cloned it into /srv/activitypub, so that's what these instructions will use), and install the requirements, with
pip install -r requirements.txt
It's a good idea to set up a virtual environment to do this in.
I set up a virtual environment in /srv/activitypub/venv.
Make sure you activate the virtual environment after creating it!
Copy activitypub_bot/settings.py.dist to activitypub_bot/settings.py, and fill in the settings, following the instructions in that file.
Copy the files in systemd_services to /etc/systemd/system, and enable them:
Copy gunicorn.conf.py.dist to gunicorn.conf.py and change it if you used different paths for the repository or the virtual environment.
Run python manage.py migrate to set up the database.
Enable the services:
systemctl enable activitypub_huey.service activitypub.service activitypub.socket
For each domain you want to run ActivityPub on, the server needs to handle requests to the URL /.well-known/webfinger and anything under /activitypub.
(You can replace /activitypub with something else if you want).
Add the following rules to each nginx config that handles domains you want to run ActivityPub on:
``` server { # ... your existing config
location ~ /activitypub/static {
rewrite ^/activitypub/static/(.*)$ /$1 break;
root /srv/activitypub/public/static;
}
location ~ /activitypub {
include proxy_params;
proxy_pass http://unix:/run/activitypub.sock;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
location = /.well-known/webfinger {
include proxy_params;
proxy_pass http://unix:/run/activitypub.sock;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
} ```
Reload nginx's config:
systemctl reload nginx
It's useful to create superuser login details for the admin interface:
python manage.py createsuperuser
If everything is set up properly, https://{DOMAIN}/activitypub/admin will show you the Django admin login screen.
You can create an actor on the command-line with the create_actor management command:
python manage.py create_actor
You'll be asked to choose the domain it should exist on, and to give a username. You can optionally create an API access token.
You can write a post through the admin interface: click on Local actors, then on the actor you want to create the post, and then click the Create a note link at the bottom of the page.
Alternately, if you created an API access token, you can make a POST request to https://{DOMAIN}/activitypub/accounts/{USERNAME}/create_note.
The request should have the header Authorization: Bearer {ACCESS_TOKEN}, and a POST parameter content with the text of the post.
When an ActivityPub message is received, it's handled by a series of subclasses of bot.inbox.AbstractInboxHandler.
For an activity with Type: "ActivityType", the corresponding method handle_ActivityType(activity) on each inbox handler will be called.
Django apps can register a new inbox handler class with bot.inbox.register_inbox_handler(cls, spec).
should_handle is either a callable of the form spec(actor, activity) which should return a boolean dictating whether the class should handle this activity received by this actor, or it should be a dictionary with keys username and domain specifying the usernames, domains, or both, whose inboxes this class should handle.
If should_handle is not given, then the handler is called for every activity.
There is a built-in inbox handler, bot.inbox.InboxHandler, which manages Follow and Like activities.
You could define an inbox handler which sends you an email whenever a Mention activity is received.
The specs are good!
The ActivityPub protocol specification gives a description of how interactions work.
]]>This extension adds a data type, spreadsheet, representing a 2D grid of text cells with styling information, similar to a spreadsheet in a program such as Microsoft Excel.
In the Numbas question editor, this extension adds a "Spreadsheet" variable template type which allows you to load a spreadsheet from an uploaded .xlsx file.
Internally, SheetJS is used to interpret spreadsheet files. Any format understood by SheetJS can be used, but style information is only loaded from .xlsx files.
When inserted into content areas, spreadsheet values are rendered as non-editable grids.
This extension adds a "Spreadsheet" answer input method for custom part types. The "expected answer" and "initial sheet" settings should be spreadsheet values with the same dimensions.
The file spreadsheet.npt defines a "Spreadsheet" custom part type which asks the student to enter values into a given spreadsheet and compares entries against a completed spreadsheet given by the question author.
spreadsheet(cells)Construct a spreadsheet object from a 2D array of cell contents.
Example: spreadsheet([["Name","Points"], ["Alice", 1], ["Harrison", 5]])
spreadsheet_from_base64_file(filename, data)Construct a spreadsheet from a .xlsx file whose contents have been encoded in base64.
This function is used by the "spreadsheet" variable template type; it's unlikely you'll ever use it in any other context.
update_range(spreadsheet, range, changes)Update the cells in the given range, following the settings in the dictionary changes.
The changes dictionary can be constructed using the styling functions below.
Example: update_range(sheet, "B2:D2", border("bottom","thick"))
disable_cells(spreadsheet, ranges)Disable cells in the given ranges, so they can't be edited by the student.
Example: disable_cells(sheet, ["A1", "C2", "B3:D5"])
fill_range(spreadsheet, range, contents)Fill in the text contents of the cells in the given range with the values from the 2D array contents.
If the range is a single cell or all the cells are in a single row or column, contents can be a sim[ple list.
Examples:
fill_range(sheet, "A1:D2", [["a","b","c","d"],[1,2,3,4]])fill_range(sheet, "A1:C1", ["a", "b", "c"])parse_range(range)Interpret a string representing a range, and return a list of the addresses of the cells it contains.
Example: parse_range("A1:B3") → [ "A1", "B1", "A2", "B2", "A3", "B3" ]
slice(spreadsheet,range)Make a copy of the given spreadsheet, containing only the cells in the given range.
Example: slice(sheet, "B2:D5")
spreadsheet[range]Return the contents of the cells in the given range, or the contents of the cell at the given address. The contents are always returned as strings. The empty string is used for empty cells.
Examples:
sheet["A1:B3"] → [["a","b"],["1","2"],["","3"]]sheet["A1"] → "a"encode_range(start_column, start_row, end_column, end_row)Given the numerical indices of the top-left and bottom-right corners of a range, return a string representing that range. The rows and columns are both numbered starting from 0.
Example: encode_range(0,0,3,2) → "A1:D3"
The following functions can be used with the update_range function to change the appearance of cells in a spreadsheet.
These functions can be chained together using the |> pipe operator.
For example: font_style("bold") |> fill("lightgreen")
Functions that specify a colour can take any valid CSS color value.
You can easily cause accessibility problems when using colour. Try not to use colour as the sole means of conveying information, or to use colour combinations that can't be easily distinguished by colourblind people.
border(sides, styles)Set cell border styles.
sides is a space-separated string giving the sides to style. The sides are named "top", "left", "bottom" and"right".
styles is a space-separated string giving the style information, in the format thickness color. thickness is one of thin, medium or thick.
Example: border("top bottom", "thin blue")
font_style(styles)Style the text content of cells.
styles is a space-separated list of style names. The understood styles are bold, italic and underline.
Example: font_style("bold underline")
font_size(size)Set the size of text in cells, as a multiple of the default size.
A size smaller than 1 can cause accessibility problems by making text hard to read; try to only make text the default size or bigger.
Example: font_size(1.2)
font_color(color)Set the color of text in cells.
Example: font_color("blue")
bg_color(color)Set the background colour of cells.
Example: bg_color("black")
horizontal_alignment(alignment)Set the horizontal alignment of text inside cells.
Any value accepted by the CSS text-align property is understood.
Example: horizontal_alignment("center")
vertical_alignment(alignment)Set the vertical alignment of text inside cells.
Any value accepted by the CSS vertical-align property is understood.
Example: vertical_alignment("middle")
This Python script takes the archive you obtain from Twitter and produces a nice static site containing all the tweets, in a form suitable for publishing online.
Install the required packages with pip install -r requirements.txt.
Extract the .zip file you get from Twitter into the same directory as this script.
Run python twitter_archive.py.
The output is in a directory called output. Copy the data/tweets_media folder from your Twitter archive to output/media.
I just made this for my own Twitter archive, and I'm not hugely interested in maintaining a generic tool for everybody. If it's useful to you as it is, then that's lovely!
]]>An interactive page to make a magic square for your birthday. Made on request for James Grime
]]>My talk at Big MathsJam 2022: five talks about trains
]]>Generates a random tree by joining up points arranged around a circle
]]>A tool to work out which browsers support a combination of features
]]>This is an iOS app for loading Numbas exams which require a locked-down environment.
]]>Provides a function written_number which converts numbers to words.
Based on js-written-number by Pedro Tacla Yamada and others, used under the terms of the MIT license.
The following languages are supported:
| Language | code |
|---------|--------|
| English | en |
| US English | en-us |
| English (Indian) | en-in |
| Portuguese (Brazil) | pt |
| Portuguese (Portugal) | pt-br |
| Spanish | es |
| French | fr |
| Esperanto | eo |
| Vietnamese | vi |
| Arabic | ar |
| Azerbaijan | az |
| Turkish | tr |
| Ukrainian | uk |
| Indonesian | id |
| Russian | ru |
written_number(n)Converts the given number to words, in the exam's preferred language (or English by default).
written_number(n,language_code)Converts the given number to words in the given language.
]]>This is a tool which runs make when a file in the current directory is changed.
You can configure it with a file called .watchmakerc in the directory you're running it from.
The file should contain some YAML in the following format:
default_make: # A list ofmake targets to run
extensions: # A list of file extensions that should trigger make. If not given, all files trigger make.
path: # A list of subdirectories to watch. If not given, all subdirectories under this one are watched.
I wrote this to learn Elixir, and to replace my existing Python script which is quite fiddly.
Build a release by running mix release.
Then the folder _build/dev/rel/watchmake contains everything needed to run the program.
Run _build/dev/rel/watchmake/bin/watchmake start to start it.
Press Ctrl+C twice to end it.
]]>A multiplayer mathematical Guess Who? game. Made for the 24 hour maths game show.
]]>An Electron app which can be used to launch resources in the Numbas LTI provider, without access to developer tools or most other browser controls.
Once you've got NodeJS installed, install yarn.
Copy config.js.dist to config.js and fill it in.
To run the browser in development mode:
yarn start open <URL>
To build the executables:
yarn build_windows
yarn build_linux
yarn build_max
It's technically possible to build the Windows executable on Linux, using Wine, but it seems to produce a much bigger package than building on Windows.
]]>A talk about Hamiltonian graphs, leading to the Herschel graph.
]]>A binary clock entirely made in CSS
]]>The game board for Maths Blockbuster, part of the 24 Hour Maths Game Show
]]>A template to quickly whip up a page to calculate a formula
]]>An animation of the shunting yard algorithm, with a little train
]]>A generator for memes, based on the Graduate Texts in Mathematics series
]]>A Wordle clone where you have to guess a sequence from the Online Encyclopedia of Integer Sequences
]]>A happy image occurred to me of the Befunge instruction pointer as a tractor, driving around a field.
]]>A game about adding and subtracting prime numbers. You start at a random number, with a random target. Try to reach the target, by adding or removing any prime factor of your current number.
]]>Reduce a book to log(2) of its original length by alternately adding and removing 1/N of the words.
]]>A WordPress plugin which provides a block to embed an interactive element. I made this to make it easier to include interactive thingies in posts on The Aperiodical.
]]>Constructive real numbers for JavaScript.
Ported from Hans-J. Boehm's Java implementation to JavaScript by Christian Lawson-Perfect.
Each recursive real number is represented as an object that provides an approximation function for the real number. The approximation function guarantees that the generated approximation is accurate to the specified precision. Arithmetic operations on constructive reals produce new such objects; they typically do not perform any real computation. In this sense, arithmetic computations are exact: They produce a description which describes the exact answer, and can be used to later approximate it to arbitrary precision.
When approximations are generated, e.g. for output, they are accurate to the requested precision; no cumulative rounding errors are visible. In order to achieve this precision, the approximation function will often need to approximate subexpressions to greater precision than was originally demanded. Thus the approximation of a constructive real number generated through a complex sequence of operations may eventually require evaluation to very high precision. This usually makes such computations prohibitively expensive for large numerical problems. But it is perfectly appropriate for use in a desk calculator, for small numerical problems, for the evaluation of expressions computated by a symbolic algebra system, for testing of accuracy claims for floating point code on small inputs, or the like.
creal.js is an ECMAScript module.
It exposes an object with a valueOf method to create a CReal from an ES number or string, and some constants.
``` import CReal from 'creal.js';
const one_seventh = CReal.valueOf(7).inverse(); console.log(one_seventh.toString(20)); ```
]]>Rectangle pals are randomly added or taken away, but after each step, all the rectangles have the same area.
]]>This is a pen-and-paper game I invented in secondary school. Twenty years later, I've made a digital version of it!
]]>This extension adds a code editor input method, and the ability to run code.
There is built-in support for running:
There is a custom part type in code.npt, which marks code entered by the student by evaluating unit tests.
The extension provides a JME function run_code.
Because the code might take a long time to run, it runs asynchronously; the function returns a promise value.
In order to use the results in marking, call run_code from the part's pre_submit note.
The rest of the marking algorithm will run once the code has finished running.
Here's an example:
pre_submit:
[
run_code(
"pyodide",
[
studentAnswer,
"'x' in locals()",
"x==1"
]
)
]
The extension provides the Ace editor as an input method for custom part types. It has three options:
textmate is bundled with the extension.run_code(language,codes)language is a string containing the name of the code runner to use.codes is a list of strings of code.Run some blocks of code and return the results in a promise.
The blocks are run one after the other, in the same global scope, so variables assigned in one block are available in subsequent blocks.
When run in the pre_submit note, this task adds an entry code_result, a list containing the results of each of the code blocks.
The result of a code block is represented in a dictionary of the following form:
result - The final value produced by the block.success - true if the code ran without errors, otherwise false.stdout - The contents of the program's STDOUT buffer. This typically contains any text printed by the program.stderr - The contents of the program's STDERR buffer. This typically contains any error messages produced by the program.error - If the code threw an error, the text of the error.variables_as_code(language, variables)Produce a block of code which assigns a series of variables in the given language.
The variables argument is a dictionary mapping variable names to values.
code_block(code,options)Display some code in a syntax-highlighted code area.
The options argument is an optional dictionary of options, or just the name of the language to use for syntax highlighting.
The following options can be set:
language - The language to use for syntax highlighting.theme - The Ace theme to use. Only the textmate theme is built in to this extension.gutter - Show or hide the gutter, which shows line numbers.language_synonym(runner)Return the name of the language corresponding to the given runner.
"pyodide" returns "python"; "webr" returns "r".
There are a few functions to produce common marking tests:
py_mark_equal(name,value,[weight=1]) - Checks that the variable with the given name has the given value. The name should be a string, but the value can be any JME value. Example: py_mark_equal('x', 1)There are a few functions to produce common validation tests:
py_valid_defined(name) - Checks that a variable with the given name has been defined - equivalent to 'name' in locals().py_valid_callable(name) - Checks that the object with the given name has been defined and is callable - equivalent to 'name' in locals() and callable(name). py_valid_isinstance(name,type) - Checks that the object with the given name has been defined and is an instance of the given type - equivalent to 'name' in locals() and isinstance(name, type).The class Numbas.extensions.programming.CodeRunner contains methods for running code in a particular language.
To add a new language, define a new class extending this one, implementing the run_code method.
Then call Numbas.extensions.programming.register_language_runner(name, runner) with the name of the runner and the class.
Only one instance of the class will ever be created.
See the PyodideRunner and WebRRunner classes in this extension for examples of how to implement a runner.
r_load_files(files)Read the contents of the given files and return the result in a promise, to be used by a pre-submit task.
When run in the pre_submit note, this task adds an entry r_files, a list containing information about each of the files:
exists - true if the file was loaded successfully, false if not.text - if the file contains text, the contents of the file as a string.blob - if the file is binary, a blob URL representing the file's contents. You can use a blob URL as the src attribute for an img or iframe tag.At the moment, only PDF files are recognised as binary. You can register other file types as binary by adding an entry to the JavaScript object Numbas.extensions.programming.mime_types.
A small game made in a morning, in Elm.
A puzzle game about shaking hands
]]>This is a command-line tool and Python package to synchronise a local folder with one in a Canvas LMS course.
In order to use this, you need a Canvas API token linked to an account which has permission to create files in your target course's Files section. See the Canvas API documentation for instructions on how to get a token.
Python 3.8 or newer is required.
To install the package, run:
pip install canvaslms_sync
This provides a shell command canvas_sync.
canvas_sync local_folder remote_url -t CANVAS_API_TOKEN
You can store your Canvas API token in a file like this:
[Canvas]
canvas_api_token = TOKEN
By default, the script looks for this file in credentials.ini in the current working directory, but you can specify a different path with the -c option.
Hidden files and folders (those whose names start with .) are ignored by default. You can include them with the --include-hidden option.
I followed the Python packaging tutorial to make this package.
To build this package, you need twine and build:
python3 -m pip install twine build
First, build the distributable files:
python3 -m build
That produces .tar.gz and .whl files in ./dist.
You can try installing the package in a different virtualenv with pip install dist/canvaslms_sync-$VERSION-py3-none-any.whl.
To upload to PyPI:
python3 -m twine upload --repository pypi dist/*
An implementation of the game Imparium, invented by Walter Joris.
Here’s a brief version of the rules: you start with a 6×6 grid of boxes. You and another player take turns removing pairs of adjacent boxes. Whenever there’s a group made up of an odd number less than 10 contiguous boxes, the last player to move can claim them. Once all boxes have been either claimed or removed, the game is over and the person who has claimed the most boxes wins.
Written in Elm.
]]>An experiment in making a calculator out of pieces that float around.
I wanted something where you could easily repeat calculations with different numbers, while being easy to use on a touch-screen device.
It's a little bit too fiddly - it takes quite a while to enter a complicated calculation, and I think it would be easier to read if the connecting lines could be bent.
]]>This repository contains everything needed to run the Chirun LTI tool in Docker containers.
There's documentation for administrators, instructors and students at TBD.
Install Docker and Docker Compose on your server.
Clone this repo somewhere on your server and cd into the directory.
Copy the file settings.env.dist to settings.env and write your own values each of the variables inside.
Obtain an SSL certificate and key for the domain you will access the CB LTI tool from.
Copy the key to files/ssl/cblti.key and the certificate to files/ssl/cblti.pem.
Run the following command in the directory where docker-compose.yml resides:
docker-compose up
Stop the containers with
docker-compose down
To use the Chirun LTI tool with your VLE, an administrator will need to add the tool to your own instance of the VLE. To do this, the VLE needs to be setup beforehand as a tool consumer in the admin panel accessible on your web server at https://chirun-lti.institution.tld/lti/admin/. Login with the administrator account setup in the file settings.env.
Create a Name, Key, and Secret for your VLE using the Add New Consumer form on the admin page, and then forward that information on to your VLE administrator to be added as an external LTI tool.
They might also need the URL for the LTI XML configuration, https://chirun-lti.institution.tld/lti/xml/ or the LTI launch URL, https://chirun-lti.institution.tld/lti/connect.php.
A simple landing page for the root of the webserver at https://chirun-lti.institution.tld has been included in files/cblti/index.html. This file is bind mounted by Docker at launch and so can be directly edited by the server administrator as required.
Docker Compose files can also be used to deploy to the cloud. See the following documents for more information about deploying Docker to the cloud: - Compose for Amazon ECS - Compose for Microsoft ACI
To upgrade to a new version, fetch the latest version of this repo and then,
docker-compose up --build
The Chirun LTI tool provides a simple to use interface for instructors to upload content to a VLE to be automatically converted into accessible and flexible documents with Chirun. With this tool, the instructor does not need to have any knowledge of running Unix software or uploading content to a web-server in order to share the web-based output generated by Chirun.
exec() function and have at least the following extensions enabled:Clone this repo and copy the software into an install directory (referred to as INSTALLDIR) visible to your web server. Copy the file config.dist.php to config.php and then edit the file to include your own site settings. Take particular note of the following variables,
Name | Description | Example
-------------|-------------|---------
WEBDIR | The intended web path for accessing the tool | /lti
INSTALLDIR | The full file system path to the tool's install directory | /var/www/webroot/lti
PROCESSUSER | The "processing user" that will run the Chirun build tools via docker | programs
DB_NAME | A database DSN specifying a database host and name | mysql:dbname=chirun;host=database.example.com
DB_USERNAME | Database username |
DB_PASSWORD | Database password |
Uploaded documents are compiled by running the Chirun software in a docker container. Since allowing access to docker is equivalent to giving root access to a user, this must be handled carefully. For separation of privileges a new Unix user must be created to be used as the Chirun processing user. This user should have access to docker via membership of the docker group.
Assuming your processing user will be named programs, the following commands creates the user and adds it to the docker group,
```
```
The user running the web server process should be able to write to UPLOADDIR, CONTENTDIR and PROCESSDIR/logs, but not permitted to write to anything else in INSTALLDIR. As such, ensure directory permissions are setup as follows, where programs is your processing user and www-data is the primary unix group for the user running the web server process.
Directory | Mode | Owner:Group
----------|------|--------------
INSTALLDIR | 755 | programs:programs
UPLOADDIR | 775 | programs:www-data
CONTENTDIR | 775 | programs:www-data
PROCESSDIR/logs |775 | programs:www-data
The sudo command should be configured so that the user running the web server process (e.g. www-data) can start the Chirun processing script as the processing user with some particular environment variables populated. This can be setup by adding the following lines to the /etc/sudoers file,
``` www-data ALL = (programs) NOPASSWD: [PROCESSDIR]/process.sh Defaults env_keep += "PROCESSDIR" Defaults env_keep += "CONTENTDIR" Defaults env_keep += "UPLOADDIR" Defaults env_keep += "DOCKER_PROCESSING_VOLUME"
```
replacing [PROCESSDIR] with the full path to the processing directory.
Prepare the docker daemon by pulling the Chirun image on the server. For example, log into the server as the processing user and run the command:
$ docker pull chirun/chirun-docker:latest
The web server's rewriting engine should be used to direct all requests of the form lti/content/[...] to lti/index.php?req_content=[...]. In addition, the following directories should be made forbidden for public access:
UPLOADIRPROCESSDIRINSTALLDIR/libThe path WEBPATH/admin should also be protected and allow only server administrator access. An example Apache setup using Basic Authentication is shown below.
<Location />
Require all granted
DirectorySlash Off
RewriteEngine on
RewriteRule /lti/content/(.*)$ /lti/index.php?req_content=$1 [L,QSA]
</Location>
<Location /lti/upload>
Require all denied
</Location>
<Location /lti/process>
Require all denied
</Location>
<Location /lti/lib>
Require all denied
</Location>
<Location /lti/admin>
AuthType Basic
AuthName "Restricted admin section"
AuthUserFile /etc/apache2/.htpasswd
Require user admin
</Location>
In this example a username and password for admin access can be setup by running the following command on the server,
```
```
To use the Chirun LTI tool with your VLE, an administrator will need to add the tool to your own instance of the VLE. To do this, the VLE needs to be setup beforehand as a tool consumer in the admin panel accessible on your web server at https://chirun.example.com/lti/admin/.
Create a Name, Key, and Secret for your VLE using the Add New Consumer form on the admin page, and then forward that information on to your VLE administrator to be added as an external LTI tool. They might also need the URL for the LTI XML configuration: https://chirun.example.com/lti/xml/ or the LTI launch URL, https://chirun.example.com/lti/connect.php.
When content is uploaded to the LTI tool for processing, the following series of steps occurs.
First, a file is uploaded to the UPLOADDIR by the web server process. On upload a GUID is generated for the uploaded bundle of files. .zip files are extracted in-place.
The processing script is started. The script runs partially in a container, and so sudo is used to start the script as the processing user with permission to use to docker.
The source content is copied from the UPLOADDIR directory to the PROCESSDIR directory.
When uploading raw .tex or .md files a standalone Chirun compatible course.yml file is created. If a Chirun config.yml file already exists as part of the upload, it is modified to inject the correct content web path and GUID.
Chirun is run in a docker container using the newly prepared course.yml in the PROCESSDIR directory.
If successful, the output is copied from the PROCESSDIR directory into the CONTENTDIR directory with the required directory permissions.
Clean up is performed and the copy of the uploaded files in PROCESSDIR are deleted.
Finally, the output of the process script is written to a log file at PROCESSDIR/logs/<guid>.log and the log is shown to the user.
The UPLOADDIR directory should be emptied periodically to avoid filling the disk. For example, this can be handled by adding a cron job or systemd timer for the programs user.
An example command that clears the upload directory of all files could be,
find [INSTALLDIR]/upload/ -mindepth 1 -maxdepth 1 -exec rm -r -- {} +
replacing [INSTALLDIR] with the full path to the install directory. The find command can be tweaked with arguments such as -mtime to more finely tune what files are deleted.
I've made another baby, so it's time for another talk about baby maths. Each Peach Pear Plum is a classic picture book for babies, with a beautifully simple rhyming scheme. But I've always wished it was more complete. Join me for an Eulerian tour through fantasy land!
]]>This extension provides some functions for working with and drawing graphs in Numbas.
Many of these functions take an adjacency matrix as an argument.
A value of 0 in position [i][j] means that there is no edge from vertex i to j; a positive value means that there is an edge, with any value less than 1 being drawn as a dashed line, and a value of 1 being drawn as a solid line.
adjacency_matrix(edges)Given a list of edges, in the form [v1,v2], where v1 and v2 are indices of vertices, produce an adjacency matrix for the graph with those edges and no extra vertices.
draw_graph_from_adjacency(adjacency, labels)Given an adjacency matrix and an optional list of string labels for the vertices, produce a drawing of a graph.
draw_graph(adjacency, vertices, labels)Given an adjacency matrix, a list of vectors giving the positions of the vertices, and an optional list of string labels for the vertices, produce a drawing of a graph.
layout_graph(adjacency)Given an adjacency matrix, lay the graph out, trying to separate vertices reasonably and avoid crossing edges.
Returns a list of vectors giving the positions of the vertices.
vertex_degrees(adjacency)Given an adjacency matrix, return a list giving the degree of each vertex.
graph_union(a,b)Given adjacency matrices a and b, return an adjacency matrix representing the union of the two graphs.
cartesian_product(a,b)Given adjacency matrices a and b, return an adjacency matrix representing the cartesian product of the two graphs.
direct_product(a,b)Given adjacency matrices a and b, return an adjacency matrix representing the direct product of the two graphs.
adjacency_permutation(p,m)Given a permutation p (produced by the permutations extension) and an adjacency matrix m, return an adjacency matrix representing the graph with the vertices permuted according to p.
is_graph_isomorphism(p,m)Given a permutation p (produced by the permutations extension) and an adjacency matrix m, return true if p is an isomorphism of the graph represented by m, and false otherwise.
All JavaScript functions are available under Numbas.extensions['graph-theory'].
Many of these functions take an adjacency matrix as an argument.
An adjacency matrix is a 2D array (an array of arrays), with added integer properties rows and columns giving the number.
A value of 0 in position [i][j] means that there is no edge from vertex i to j; a positive value means that there is an edge, with any value less than 1 being drawn as a dashed line, and a value of 1 being drawn as a solid line.
connected_components(adjacency)Given an adjacency matrix, find the connected components. Returns a list of components, each represented as a list of the indices of the vertices in that component.
is_connected(adjacency)Return true if the graph represented by the given adjacency matrix has a single connected component.
subgraph(adjacency,vertices)Given a graph represented by an adjacency matrix, and a list of indices of vertices, return the adjacency matrix of the subgraph containing only those vertices.
largest_connected_component(adjacency, labels)Given a graph represented as an adjacency matrix, and a list of labels for the vertices, return the largest connected component of the graph and the labels of its vertices.
Returns an object {adjacency, labels}.
(I'm not sure if this function is needed)
adjacency_matrix_from_edges(edges,directed)Given a list of edges, produce the corresponding adjacency matrix.
edges is a list of values [v1,v2], where v1 and v2 are indices of vertices.
If directed is true, then the adjacency matrix will be made symmetric about its diagonal.
So for an undirected edge between vertices i and j, you only need to include [i,j] in edges.
If directed is false, then [i,j] and [j,i] are different directed edges.
from_adjacency_matrix(adjacency)Given an adjacency matrix, return lists of vertices and edges, to be used by the cola layout engine.
Returns an object {vertices, edges}.
draw_graph(graph)graph is an object {vertices, adjacency, labels}.
vertices is a list of objects {x,y}.adjacency is an adjacency matrix: 0 means no edge; 1 means an edge; and any number between 0 and 1 means a dashed edge. If there are edges i to j and j to i, the edge is drawn as undirected; otherwise if there is just an edge i to j it is drawn as directed with an arrow.labels is a list of string labels for the vertices.Returns an SVG element containing a drawing of the graph.
layout_graph(graph)graph is an object {vertices, edges}.
vertices is a list of objects {x,y}.edges is a list of objects {source, target, weight}, where source and target are indices of vertices, and weight is a scalar controlling how long the edge should be.Returns a list of positions for the vertices, as objects {x,y}.
vertex_degrees(adjacency)Given a graph represented as an adjacency matrix, return a list giving the degree of each vertex.
graph_union(m1, m2, ...)Given arbitrarily many adjacency matrices representing graphs, return an adjacency matrix representing the union of those graphs.
cartesian_product(m1, m2, ...)Given arbitrarily many adjacency matrices representing graphs, return an adjacency matrix representing the cartesian product of those graphs.
direct_product(m1, m2, ...)Given arbitrarily many adjacency matrices representing graphs, return an adjacency matrix representing the direct product of those graphs.
adjacency permutation(p,m)Apply the given permutation p, represented as a list where p[i] gives the image of i under p, to the vertices of the graph represented by the adjacency matrix m.
Returns an adjacency matrix.
is_graph_isomorphism(p,m)Returns true if the permutation p is an isomorphism of the graph represented by the adjacency matrix m.
edges_from_weight_matrix(weights)weights is a 2D array, where weights[i][j] gives the weight of the edge between vertices i and j, or -1 if there's no edge.
Returns a list of edges represented as objects {from, to, weight}.
kruskals_algorithm(weights)Find a minimum spanning forest of the graph represented by weights, using Kruskal's algorithm.
weights is a 2D array representing the weights of edges in a graph, which will be passed to edges_from_weight_matrix to produce a list of edges.
Returns a list of edges, each represented by an object {from, to, weight}.
prims_algorithm(weights)Find a minimum spanning tree of the graph represented by weights, using Prim's algorithm.
The graph must be connected.
weights is a 2D array representing the weights of edges in a graph, which will be passed to edges_from_weight_matrix to produce a list of edges.
Returns a list of edges, each represented by an object {from, to, weight}.
I'm really colourblind. There are quite a few apps that let you point your camera at something and tell you what colour it is, but they're either too erratic or too precise in the names of colours they give.
After years of frustration, I decided to make my own. This does a few things:
In this repository I'm collecting essays and documentation on the design decisions behind the Numbas system.
The notes are written using Sphinx. Install it using pip:
pip install -r requirements.txt
To build the HTML version, run:
make html
A tool to convert QTI packages to Numbas exams.
This is an ambitious goal! At the moment, it can convert the following kinds of packages:
It could be expanded to support more of the QTI specification.
There is no real error handling: packages containing unsupported items will fail.
I (Christian Lawson-Perfect) don't have access to Blackboard, so if you have a QTI package that doesn't work, please send it to me and I'll try to work out how to support it.
This is a Python 3 script which requires a couple of packages to be installed.
To install the packages, run:
pip install -r requirements.txt
To convert a package called question_bank.zip, run:
python qti_to_numbas.py question_bank.zip
A .exam file containing all of the questions in the question bank will be created in the current directory. You can upload this file to the Numbas editor.
My son was born last September. While he doesn’t hate sleep as much as his sister did, he still needs a bit of help to drop off. I’m not at all musically inclined, and I seem unable to …
]]>A collaborative site collecting ambiguities, inconsistencies and other unpleasantness in the conventions of mathematical notation.
]]>A game where you're shown a Unicode character and have to work out its name
]]>A wordsearch generator, written in Elm
]]>Can you make a rule for laying out a pentagonal spiral, so it goes on forever?
]]>A Jupyter notebook to accompany a post on The Aperiodical by Connor Krill. It shows that computers can find Mersenne primes much faster than Édouard Lucas (Connor got the wrong mathematician originally).
]]>For my birthday I got an EleksDraw pen plotter. It’s a cheap and cheerful example of the form: a pair of orthogonal metal rods with a pen on the end, attached to electric motors. The idea is …
]]>A web page to create a postcard, for my pen plotter to draw. The text can include mathematical notation, rendered using MathJax.
]]>A tool to make 'Which One Doesn't Belong?' grids - a 2×2 square of images.
]]>A page made to show how to embed a Numbas exam and extract the attempt data from it.
]]>This is a web-based controller for my pen plotter, an EleksDraw.
It uses vpype to read in SVG files and produce a load of line segments, then sends GCode to a plotter device connected on a serial port, which it tries to detect.
There are two processes that need to run: the web server, and the worker which talks to the plotter.
To run the web server:
python manage.py runserver
To run the worker:
python manage.py runworker plotter-manager
This repository contains files for the camera operator at MathsKlatch 2020.
bookmarklet.js contains a JavaScript bookmarklet which will toggle the intruding bits of the gather.town UI off, so you can capture just the speaker's video.
MathsJam_2020.obs is an OBS scene collection. One scene is configured to capture a window, and there are two other scenes containing images to switch to while fiddling with things. I don't think this will copy across to another device well, but at least it's a starting point.
]]>This extension provides a function download_link(filename,content,link_text), which creates an HTML link tag with the given link tag. When the student clicks on the link, a file with the given filename and content is downloaded.
There's a demo question showing how to use this extension.
download_link(filename,content,link_text)Creates an HTML link tag which downloads a file with the given filename and text content. If you don't give link_text, the link reads "Download filename".
csv(data,headers)Format a set of data as a CSV file.
data is a list of lists, The second argument headers is an optional list of strings to use as column headers.data is a list of dictionaries, the output contains a column for each distinct key in those dictionaries.data is a dictionary, it's assumed to map headers to columns of data.Inspired by a question on math-fun.
]]>I had a thought about using Blockly to construct proofs, which would be translated to Lean code for evaluation.
]]>Earlier this year, when getting the train to work was still a thing for me, I noticed this statistic: 95% of the time escalators were working in the last four weeks.
]]>A clock which shows a different permutation of a deck of cards each second, for the next 10^60 years
]]>A different permutation of the cards every second.
]]>transcriptpage is a tool which takes a video and a captions file, and creates a web page which displays the video alongside the transcript.
Each sentence in the transcript is displayed on a separate line, along with a timestamp; clicking on the timestamp jumps the video to that sentence.
transcriptpage is a Python 3 script. It has been tested with python 3.8.
At the moment, it only supports videos on Vimeo. Soon, it will support YouTube as well.
It requires a few Python packages, which can be installed with pip:
python
pip install -r requirements.txt
To create a page, you need a captions file in .srt format and the address of the video. The command takes two arguments: the local path of the .srt file, and the address of the video.
./transcriptpage captions.srt https://vimeo.com/442721457
A talk given at EAMS 2020. Presented both as a video and a series of slides with accompanying transcript.
]]>A memory game that is bigger than you'll ever be able to deal with
]]>A wobbly clock
]]>This Moodle plugin provides a special report for Moodle's SCORM player, for use with Numbas exams.
For each attempt, it shows the student's answers and scores against the correct answer for each part, in an easier to read form than Moodle's built-in "track details" view.
Clone this repository into the folder mod/scorm/report/numbas inside your Moodle folder, then go to Site administration → Notifications and click to install it.
Click on a Numbas SCORM package, then click Reports → Numbas report.
Click on the attempt number for the attempt you want to look at. You'll be shown a breakdown of the student's answers and scores for that attempt.
Copyright (C) 2020-2021 Newcastle University
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, either version 3 of the License, or (at your option) any later version.
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, see https://www.gnu.org/licenses/.
]]>If you wanted to give the same amount to everyone in a place, how much would each person get?
]]>make, which runs this:
docker build . --build-arg VERSION=(latest commit hash)

Chirun produces flexible and accessible course notes, in a variety of formats, from LaTeX or Markdown source. It is aimed primarily at notes in the mathematical sciences.
This repository, chirun, is the source code of the Chirun Python package, providing the command line interface for building content.
The Chirun documentation, including infromation about the Chirun Public Content Builder and Chirun LTI Provider can be found at,
https://chirun.readthedocs.io/en/latest/
A set of course notes are provided in either Markdown or LaTeX along with a configuration file config.yml. The chirun command then builds the requested outputs based on the contents of the configuration file.
apt install texlive-full).pdf2svg, pdftoppm, pdftk and libyaml using your standard package manager (apt install pdf2svg poppler-utils libyaml-dev pdftk-java).virtualenv python package is installed (apt install python3-virtualenv).brew command to install pdf2svg, pdftoppm, pdftk and libyaml:brew install popplerbrew install pdf2svgbrew install libyamlbrew install pdftk-javavirtualenv by running pip3 install virtualenv./Applications/Python 3.X, where 3.X is the version),
ensure that the SSL CA certificates are installed by running:sudo /Applications/Python\ 3.X/Install\ Certificates.commandvirtualenv -p python3 chirun_env and activate it: source ./chirun_env/bin/activatepip install git+https://github.com/chirun-ncl/chirun.gitThe command chirun is now available for use. You should now compile the sample course and ensure everything works.
chirun:pip install --upgrade git+https://github.com/chirun-ncl/chirun.git--force-reinstall argument if the version number has not been changed between updates.virtualenv -p python3 chirun_env and activate it: source ./chirun_env/bin/activategit clone https://github.com/chirun-ncl/chirun.gitcd chirunpip install -r requirements.txtpip install -e .The command chirun is now available for use. You should now compile the sample course and ensure everything works.
cd chirungit pullpip install -r requirements.txtmakecourseThe project has recently been renamed from "makecourse" to "chirun". To upgrade, first remove the older makecourse package with,
pip uninstall makecourse
Then follow the installation or upgrade instructions above.
Sample course, and its source code.
chirun package using the instructions abovegit clone https://github.com/chirun-ncl/sample_course.gitmake to build and view a local version of the sample course../sample_course/buildSome looping animations that I made.
]]>A tool to play around with the order of operations.
Pretty much everyone is taught a mnemonic for the order of arithmetic operations.
Some people are taught BODMAS, some are taught BIMDAS, while PEMDAS is popular in other parts of the world.
They describe the order in which you should carry out operations, but you need some more rules as well, that don't fit into a snappy mnemonic:
Except multiplication and division, and addition and subtraction, have the same precedence: you consider them both at the same time, rather than one after the other. The mnemonic doesn't mention this.
It's actually quite fiddly, but the fiddliness rarely makes a difference, if you've got common sense.
What if you didn't have any common sense?
SAMDOB lets you see how the order of operations is applied. When you write an expression in the right-hand box, the steps taken to evaluate it to a number are shown underneath.
You can change the order of operations by writing a mnemonic in the left-hand box. Once you've done that, the sequence is described underneath.
Letters grouped in brackets have the same precedence.
]]>Last year I wrote about a 3D-printed puzzle I’d designed, called Seven Triples. At work we want to use this puzzle during an A-Level enrichment day, which means we need about twenty copies of…
]]>A nice looping animation showing a triangular lattice morphing to a square grid
]]>A nice looping animation showing a triangular lattice morphing to a square grid
]]>Game/puzzle to do with shuffling boxes
]]>Draws the Conant gasket, a fractal which ought to be better known.
]]>I was trying to make something to visualise the flow of parts in a Numbas explore mode question.
]]>Jim Fowler's TikZJax is incredible, but it needed an easy way of trying it out. So I made one.
]]>Uses some word lists to come up with random fake place names and show them in street signs. Prompted by the thought that glitch's automatic project URLs would be cuter if they were place names.
]]>At work we’ve got a 3D printer. In this series of posts I’ll share some of the designs I’ve made. There are seven kinds of shape. There are three copies of each shape. The pieces …
]]>You can impose on Sudoku puzzles a physical system which works surprisingly well. It has phase transitions, and when you reduce the heat it settles into a solved state!
]]>An arty thing inspired by the Herschel enneahedron.
The genesis of this was thinking about how to show that the Herschel graph is non-Hamiltonian: there's no path that visits every vertex once.
This simulates flocking gliders, who move between the points on the graph. Because the graph is non-Hamiltonian, every glider is bound to re-visit a vertex before they've completed the tour.
]]>Lots and lots and lots of overlapping rings
]]>A puzzle from the maths-fun mailing list.
]]>All the numbers have come to a party in fancy dress.
]]>This is known in some places as the chaos game: if you repeatedly move halfway towards a randomly chosen vertex of an equilateral triangle, the positions you can end up together make the Sierpiński triangle. In this one, press the keys 1, 2 and 3 to move to each vertex.
]]>An adaptation of the book Solo Noughts and Crosses
]]>About the Big Internet Math-Off, a competition I ran on The Aperiodical. Given at Talking Maths in Public.
]]>Inspired by the 'paralellepiped incident' joke that some friends make. You reocrd an incident, and when you come back later it tells you how long since the last incident.
]]>An RPN calculator with some nifty features.
This extension for Numbas contains a port of the Eukleides geometrical drawing language by Christian Obrecht, with extensions to make diagrams interactive and accessibility improvements.
There's documentation on how the extension works and a full function reference.
See the demo page for a gallery of examples, and the interactive playground to draw your own diagrams.
The extension is written using ES6 syntax and features. This is compiled with babeljs to ES5, which is supported by lots more browsers.
dev_demo.html and dev_playground.html load the source scripts directly, on browsers which support ES6.
To install the babeljs tools, first install nodejs and then run:
npm install
Then, when you make a change to the source, run
make
to compile the scripts in the dist folder.
You might need to set the environment variable NUMBAS_RUNTIME_PATH to the location of your Numbas compiler directory.
© 2019 Christian Lawson-Perfect for Newcastle University.
The original Eukleides is © 2004-2010 Christian Obrecht.
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; either version 3 of the License, or (at your option) any later version.
Some code is taken from g9, and used under the terms of the MIT license.
Data from ColorBrewer2 is used under the terms of the Apache 2.0 license.
]]>I've made a few mathsy t-shirt designs.
]]>This extension provides a load of statistical functions, wrapping the jStat library.
There's documentation at docs.numbas.org.uk.
This list of functions contains descriptions copied from the jStat documentation. Click on the function name to see the original documentation.
sum(array)Returns the sum of the array vector.
sumsqrd(array)Returns the sum squared of the array vector.
sumsqerr(array)Returns the sum of squared errors of prediction of the array vector.
product(array)Returns the product of the array vector.
min(array)Returns the minimum value of the array vector.
max(array)Returns the maximum value of the array vector.
mean(array)Returns the mean of the array vector.
meansqerr(array)Returns the mean squared error of the array vector.
geomean(array)Returns the geometric mean of the array vector.
median(array)Returns the median of the array vector.
cumsum(array)Returns an array of partial sums in the sequence.
diff(array)Returns an array of the successive differences of the array.
range(array)Returns the range of the array vector.
variance(array)Returns the variance of the array vector.
By default, the population variance is calculated.
Passing true to flag indicates to compute the sample variance instead.
stdev(array)Returns the standard deviation of the array vector.
By default, the population standard deviation is returned.
Passing true to flag returns the sample standard deviation.
meandev(array)Returns the mean absolute deviation of the array vector.
meddev(array)Returns the median absolute deviation of the array vector.
coeffvar(array)Returns the coefficient of variation of the array vector.
quartiles(array)Returns the quartiles of the array vector.
covariance(array1, array2)Returns the covariance of the array1 and array2 vectors.
corrcoeff(array1, array2)Returns the population correlation coefficient of the array1 and array2 vectors (Pearson's Rho).
stdev(array, is_sample)Returns the standard deviation of the array vector.
By default, the population standard deviation is returned.
Passing true to flag returns the sample standard deviation.
variance(array, is_sample)Returns the variance of the array vector.
By default, the population variance is calculated.
Passing true to flag indicates to compute the sample variance instead.
mode(array)Returns the mode of the array vector.
If there are multiple modes then mode() will return all of them.
betapdf(x, alpha, beta)Returns the value of x in the Beta distribution with parameters alpha and beta.
betacdf(x, alpha, beta)Returns the value of x in the cdf for the Beta distribution with parameters alpha and beta.
betainv(p, alpha, beta)Returns the value of p in the inverse of the cdf for the Beta distribution with parameters alpha and beta.
betamean(alpha, beta)Returns the mean of the Beta distribution with parameters alpha and beta.
betamedian(alpha, beta)Returns the median of the Beta distribution with parameters alpha and beta.
betamode(alpha, beta)Returns the mode of the Beta distribution with parameters alpha and beta.
betasample(alpha, beta)Returns a random number whose distribution is the Beta distribution with parameters alpha and beta.
betavariance(alpha, beta)Returns the variance of the Beta distribution with parameters alpha and beta.
centralFpdf(x, df1, df2)Given x in the range [0, infinity), returns the probability density of the (central) F distribution at x.
centralFcdf(x, df1, df2)Given x in the range [0, infinity), returns the cumulative probability density of the central F distribution. That is, jStat.centralF.cdf(2.5, 10, 20) will return the probability that a number randomly selected from the central F distribution with df1 = 10 and df2 = 20 will be less than 2.5.
centralFinv(p, df1, df2)Given p in [0, 1), returns the value of x for which the cumulative probability density of the central F distribution is p. That is, jStat.centralF.inv(p, df1, df2) = x if and only if jStat.centralF.inv(x, df1, df2) = p.
centralFmean(df1, df2)Returns the mean of the (Central) F distribution.
centralFmode(df1, df2)Returns the mode of the (Central) F distribution.
centralFsample(df1, df2)Returns a random number whose distribution is the (Central) F distribution.
centralFvariance(df1, df2)Returns the variance of the (Central) F distribution.
cauchypdf(x, local, scale)Returns the value of x in the pdf of the Cauchy distribution with a location (median) of local and scale factor of scale.
cauchycdf(x, local, scale)Returns the value of x in the cdf of the Cauchy distribution with a location (median) of local and scale factor of scale.
cauchyinv(p, local, scale)Returns the value of p in the inverse of the cdf for the Cauchy distribution with a location (median) of local and scale factor of scale.
cauchymedian(local, scale)Returns the value of the median for the Cauchy distribution with a location (median) of local and scale factor of scale.
cauchymode(local, scale)Returns the value of the mode for the Cauchy distribution with a location (median) of local and scale factor of scale.
cauchysample(local, scale)Returns a random number whose distribution is the Cauchy distribution with a location (median) of local and scale factor of scale.
chisquarepdf(x, dof)Returns the value of x in the pdf of the Chi Square distribution with dof degrees of freedom.
chisquarecdf(x, dof)Returns the value of x in the cdf of the Chi Square distribution with dof degrees of freedom.
chisquareinv(p, dof)Returns the value of x in the inverse of the cdf for the Chi Square distribution with dof degrees of freedom.
chisquaremean(dof)Returns the value of the mean for the Chi Square distribution with dof degrees of freedom.
chisquaremedian(dof)Returns the value of the median for the Chi Square distribution with dof degrees of freedom.
chisquaremode(dof)Returns the value of the mode for the Chi Square distribution with dof degrees of freedom.
chisquaresample(dof)Returns a random number whose distribution is the Chi Square distribution with dof degrees of freedom.
chisquarevariance(dof)Returns the value of the variance for the Chi Square distribution with dof degrees of freedom.
exponentialpdf(x, rate)Returns the value of x in the pdf of the Exponential distribution with the parameter rate (lambda).
exponentialcdf(x, rate)Returns the value of x in the cdf of the Exponential distribution with the parameter rate (lambda).
exponentialinv(p, rate)Returns the value of p in the inverse of the cdf for the Exponential distribution with the parameter rate (lambda).
exponentialmean(rate)Returns the value of the mean for the Exponential distribution with the parameter rate (lambda).
exponentialmedian(rate)Returns the value of the median for the Exponential distribution with the parameter rate (lambda)
exponentialmode(rate)Returns the value of the mode for the Exponential distribution with the parameter rate (lambda).
exponentialsample(rate)Returns a random number whose distribution is the Exponential distribution with the parameter rate (lambda).
exponentialvariance(rate)Returns the value of the variance for the Exponential distribution with the parameter rate (lambda).
gammapdf(x, shape, scale)Returns the value of x in the pdf of the Gamma distribution with the parameters shape (k) and scale (theta). Notice that if using the alpha beta convention, scale = 1/beta.
gammacdf(x, shape, scale)Returns the value of x in the cdf of the Gamma distribution with the parameters shape (k) and scale (theta). Notice that if using the alpha beta convention, scale = 1/beta.
gammainv(p, shape, scale)Returns the value of p in the inverse of the cdf for the Gamma distribution with the parameters shape (k) and scale (theta). Notice that if using the alpha beta convention, scale = 1/beta.
gammamean(shape, scale)Returns the value of the mean for the Gamma distribution with the parameters shape (k) and scale (theta). Notice that if using the alpha beta convention, scale = 1/beta.
gammamode(shape, scale)Returns the value of the mode for the Gamma distribution with the parameters shape (k) and scale (theta). Notice that if using the alpha beta convention, scale = 1/beta.
gammasample(shape, scale)Returns a random number whose distribution is the Gamma distribution with the parameters shape (k) and scale (theta). Notice that if using the alpha beta convention, scale = 1/beta.
gammavariance(shape, scale)Returns the value of the variance for the Gamma distribution with the parameters shape (k) and scale (theta). Notice that if using the alpha beta convention, scale = 1/beta.
invgammapdf(x, shape, scale)Returns the value of x in the pdf of the Inverse-Gamma distribution with parametres shape (alpha) and scale (beta).
invgammacdf(x, shape, scale)Returns the value of x in the cdf of the Inverse-Gamma distribution with parametres shape (alpha) and scale (beta).
invgammainv(p, shape, scale)Returns the value of p in the inverse of the cdf for the Inverse-Gamma distribution with parametres shape (alpha) and scale (beta).
invgammamean(shape, scale)Returns the value of the mean for the Inverse-Gamma distribution with parametres shape (alpha) and scale (beta).
invgammamode(shape, scale)Returns the value of the mode for the Inverse-Gamma distribution with parametres shape (alpha) and scale (beta).
invgammasample(shape, scale)Returns a random number whose distribution is the Inverse-Gamma distribution with parametres shape (alpha) and scale (beta).
invgammavariance(shape, scale)Returns the value of the variance for the Inverse-Gamma distribution with parametres shape (alpha) and scale (beta).
kumaraswamypdf(x, alpha, beta)Returns the value of x in the pdf of the Kumaraswamy distribution with parameters a and b.
kumaraswamycdf(x, alpha, beta)Returns the value of x in the cdf of the Kumaraswamy distribution with parameters alpha and beta.
kumaraswamyinv(p, alpha, beta)Returns the value of p in the inverse of the pdf for the Kumaraswamy distribution with parametres alpha and beta.
kumaraswamymean(alpha, beta)Returns the value of the mean of the Kumaraswamy distribution with parameters alpha and beta.
kumaraswamymedian(alpha, beta)Returns the value of the median of the Kumaraswamy distribution with parameters alpha and beta.
kumaraswamymode(alpha, beta)Returns the value of the mode of the Kumaraswamy distribution with parameters alpha and beta.
kumaraswamyvariance(alpha, beta)Returns the value of the variance of the Kumaraswamy distribution with parameters alpha and beta.
lognormalpdf(x, mu, sigma)Returns the value of x in the pdf of the Log-normal distribution with paramters mu (mean) and sigma (standard deviation).
lognormalcdf(x, mu, sigma)Returns the value of x in the cdf of the Log-normal distribution with paramters mu (mean) and sigma (standard deviation).
lognormalinv(p, mu, sigma)Returns the value of x in the inverse of the cdf for the Log-normal distribution with paramters mu (mean of the Normal distribution) and sigma (standard deviation of the Normal distribution).
lognormalmean(mu, sigma)Returns the value of the mean for the Log-normal distribution with paramters mu (mean of the Normal distribution) and sigma (standard deviation of the Normal distribution).
lognormalmedian(mu, sigma)Returns the value of the median for the Log-normal distribution with paramters mu (mean of the Normal distribution) and sigma (standard deviation of the Normal distribution).
lognormalmode(mu, sigma)Returns the value of the mode for the Log-normal distribution with paramters mu (mean of the Normal distribution) and sigma (standard deviation of the Normal distribution).
lognormalsample(mu, sigma)Returns a random number whose distribution is the Log-normal distribution with paramters mu (mean of the Normal distribution) and sigma (standard deviation of the Normal distribution).
lognormalvariance(mu, sigma)Returns the value of the variance for the Log-normal distribution with paramters mu (mean of the Normal distribution) and sigma (standard deviation of the Normal distribution).
normalpdf(x, mean, std)Returns the value of x in the pdf of the Normal distribution with parameters mean and std (standard deviation).
normalcdf(x, mean, std)Returns the value of x in the cdf of the Normal distribution with parameters mean and std (standard deviation).
normalinv(p, mean, std)Returns the value of p in the inverse cdf for the Normal distribution with parameters mean and std (standard deviation).
normalmean(mean, std)Returns the value of the mean for the Normal distribution with parameters mean and std (standard deviation).
normalmedian(mean, std)Returns the value of the median for the Normal distribution with parameters mean and std (standard deviation).
normalmode(mean, std)Returns the value of the mode for the Normal distribution with parameters mean and std (standard deviation).
normalsample(mean, std)Returns a random number whose distribution is the Normal distribution with parameters mean and std (standard deviation).
normalvariance(mean, std)Returns the value of the variance for the Normal distribution with parameters mean and std (standard deviation).
paretopdf(x, scale, shape)Returns the value of x in the pdf of the Pareto distribution with parameters scale (x<sub>m</sub>) and shape (alpha).
paretocdf(x, scale, shape)Returns the value of x in the cdf of the Pareto distribution with parameters scale (x<sub>m</sub>) and shape (alpha).
paretoinv(p, scale, shape)Returns the inverse of the Pareto distribution with probability p, scale, shape.
paretomean(scale, shape)Returns the value of the mean of the Pareto distribution with parameters scale (x<sub>m</sub>) and shape (alpha).
paretomedian(scale, shape)Returns the value of the median of the Pareto distribution with parameters scale (x<sub>m</sub>) and shape (alpha).
paretomode(scale, shape)Returns the value of the mode of the Pareto distribution with parameters scale (x<sub>m</sub>) and shape (alpha).
paretovariance(scale, shape)Returns the value of the variance of the Pareto distribution with parameters scale (x<sub>m</sub>) and shape (alpha).
studenttpdf(x, dof)Returns the value of x in the pdf of the Student's T distribution with dof degrees of freedom.
studenttcdf(x, dof)Returns the value of x in the cdf of the Student's T distribution with dof degrees of freedom.
studenttinv(p, dof)Returns the value of p in the inverse of the cdf for the Student's T distribution with dof degrees of freedom.
studenttmean(dof)Returns the value of the mean of the Student's T distribution with dof degrees of freedom.
studenttmedian(dof)Returns the value of the median of the Student's T distribution with dof degrees of freedom.
studenttmode(dof)Returns the value of the mode of the Student's T distribution with dof degrees of freedom.
studenttsample(dof)Returns a random number whose distribution is the Student's T distribution with dof degrees of freedom.
studenttvariance(dof)Returns the value of the variance for the Student's T distribution with dof degrees of freedom.
weibullpdf(x, scale, shape)Returns the value x in the pdf for the Weibull distribution with parameters scale (lambda) and shape (k).
weibullcdf(x, scale, shape)Returns the value x in the cdf for the Weibull distribution with parameters scale (lambda) and shape (k).
weibullinv(p, scale, shape)Returns the value of x in the inverse of the cdf for the Weibull distribution with parameters scale (lambda) and shape (k).
weibullmean(scale, shape)Returns the value of the mean of the Weibull distribution with parameters scale (lambda) and shape (k).
weibullmedian(scale, shape)Returns the value of the median of the Weibull distribution with parameters scale (lambda) and shape (k).
weibullmode(scale, shape)Returns the mode of the Weibull distribution with parameters scale (lambda) and shape (k).
weibullsample(scale, shape)Returns a random number whose distribution is the Weibull distribution with parameters scale (lambda) and shape (k).
weibullvariance(scale, shape)Returns the variance of the Weibull distribution with parameters scale (lambda) and shape (k).
uniformpdf(x, a, b)Returns the value of x in the pdf of the Uniform distribution from a to b.
uniformcdf(x, a, b)Returns the value of x in the cdf of the Uniform distribution from a to b.
uniforminv(p, a, b)Returns the inverse of the uniform.cdf function; i.e. the value of x for which uniform.cdf(x, a, b) == p.
uniformmean(a, b)Returns the value of the mean of the Uniform distribution from a to b.
uniformmedian(a, b)Returns the value of the median of the Uniform distribution from a to b.
uniformmode(a, b)Returns the value of the mode of the Uniform distribution from a to b.
uniformsample(a, b)Returns a random number whose distribution is the Uniform distribution from a to b.
uniformvariance(a, b)Returns the variance of the Uniform distribution from a to b.
binomialpdf(x, n, p)Returns the value of k in the pdf of the Binomial distribution with parameters n and p.
binomialcdf(x, n, p)Returns the value of k in the cdf of the Binomial distribution with parameters n and p.
geometricpdf(x, p)geometriccdf(x, p)geometricmean(p)geometricmedian(p)geometricmode(p)geometricsample(p)geometricvariance(p)negbinpdf(x, r, p)Returns the value of k in the pdf of the Negative Binomial distribution with parameters n and p.
negbincdf(x, r, p)Returns the value of x in the cdf of the Negative Binomial distribution with parameters n and p.
hypgeompdf(x, population_size, success_rate, num_draws)Returns the value of k in the pdf of the Hypergeometric distribution with parameters N (the population size), m (the success rate), and n (the number of draws).
hypgeomcdf(x, population_size, success_rate, num_draws)Returns the value of x in the cdf of the Hypergeometric distribution with parameters N (the population size), m (the success rate), and n (the number of draws).
poissonpdf(x, l)Returns the value of k in the pdf of the Poisson distribution with parameter l (lambda).
poissoncdf(x, l)Returns the value of x in the cdf of the Poisson distribution with parameter l (lambda).
poissonmean(l)poissonsample(l)Returns a random number whose distribution is the Poisson distribution with rate parameter l (lamda)
poissonvariance(l)triangularpdf(x, a, b, c)Returns the value of x in the pdf of the Triangular distribution with the parameters a, b, and c.
triangularcdf(x, a, b, c)Returns the value of x in the cdf of the Triangular distribution with the parameters a, b, and c.
triangularinv(p, a, b, c)triangularmean(a, b, c)Returns the value of the mean of the Triangular distribution with the parameters a, b, and c.
triangularmedian(a, b, c)Returns the value of the median of the Triangular distribution with the parameters a, b, and c.
triangularmode(a, b, c)Returns the value of the mode of the Triangular distribution with the parameters a, b, and c.
triangularsample(a, b, c)Returns a random number whose distribution is the Triangular distribution with the parameters a, b, and c.
triangularvariance(a, b, c)Returns the value of the variance of the Triangular distribution with the parameters a, b, and c.
zScore(value, mean, sd)Returns the z-score of value given the data from array. flag===true denotes
use of the sample standard deviation.
zScore(value, array)Returns the z-score of value given the data from array. flag===true denotes
use of the sample standard deviation.
zTest(value, mean, sd, sides)Returns the p-value of value given the data from array. sides is
an integer value 1 or 2 denoting a one or two sided z-test. If sides
is not specified the test defaults to a two sided z-test. flag===true
denotes the use of the sample standard deviation.
zTest(zscore, sides)Returns the p-value of value given the data from array. sides is
an integer value 1 or 2 denoting a one or two sided z-test. If sides
is not specified the test defaults to a two sided z-test. flag===true
denotes the use of the sample standard deviation.
tScore(value, mean, sd, n)Returns the t-score of value given the data from array.
tScore(value, array)Returns the t-score of value given the data from array.
tTest(value, mean, sd, n, sides)Returns the p-value of value given the data in array.
sides is an integer value 1 or 2 denoting a one or two sided
t-test. If sides is not specified the test defaults to a two
sided t-test.
tTest(tscore, n, sides)Returns the p-value of value given the data in array.
sides is an integer value 1 or 2 denoting a one or two sided
t-test. If sides is not specified the test defaults to a two
sided t-test.
tTest(value, array, sides)Returns the p-value of value given the data in array.
sides is an integer value 1 or 2 denoting a one or two sided
t-test. If sides is not specified the test defaults to a two
sided t-test.
anovaFScore(array1, ..., arrayN)Returns the f-score of an ANOVA on the arrays.
anovaFTest(array1, ..., arrayN)Returns the p-value of the f-statistic from the ANOVA test on the arrays.
ftest(fscore, df1, df2)Returns the p-value for the fscore f-score with a df1 numerator degrees
of freedom and a df2 denominator degrees of freedom.
normalci(value, alpha, sd, n)Returns a 1-alpha confidence interval for value given
a normal distribution in the data from array.
normalci(value, alpha, array)Returns a 1-alpha confidence interval for value given
a normal distribution in the data from array.
tci(value, alpha, sd, n)Returns a 1-alpha confidence interval for value given
the data from array.
tci(value, alpha, array)Returns a 1-alpha confidence interval for value given
the data from array.
betafn(x, y)Evaluates the Beta function at (x,y).
betaln(x, y)Evaluates the log Beta function at (x,y).
betacf(x, a, b)Returns the continued fraction for the incomplete Beta function with parameters a and b modified by Lentz's method evaluated at x.
ibetainv(p, a, b)Returns the inverse of the incomplete Beta function evaluated at (p,a,b).
ibeta(x, a, b)Returns the incomplete Beta function evaluated at (x,a,b).
gammaln(x)Returns the Log-Gamma function evaluated at x.
gammafn(x)Returns the Gamma function evaluated at x. This is sometimes called the 'complete' gamma function.
gammap(a, x)Returns the lower incomplete gamma function evaluated at (a,x).
This function is usually written with a lower case greek gamma character, and is one of the two incomplete gamma functions.
factorialln(n)Returns the natural log factorial of n.
factorial(n)Returns the factorial of n.
combination(n, m)Returns the number of combinations of n, m.
permutation(permutation)Returns the number of permutations of n, m.
gammapinv(p, a)Returns the inverse of the lower regularized incomplete Gamma function evaluated at (p,a).
This function is the inverse of lowerRegularizedGamma(x, a).
erf(x)Returns the error function evaluated at x.
erfc(x)Returns the complementary error function evaluated at x.
erfcinv(p)Returns the inverse of the complementary error function evaluated at p.
randn(n, m)Returns a normal deviate (mean 0 and standard deviation 1).
randg(shape, n, m)Returns a Gamma deviate by the method of Marsaglia and Tsang.
]]>This extension provides the JSXGraph library and functions to create diagrams inside a Numbas question.
The JSXGraph demo exam contains a collection of questions demonstrating different techniques to do with JSXGraph.
The preferred method to create a diagram is one of the jsxgraph or jessiecode functions. These produce a value with the type jsxgraphboard, which can be inserted into content areas by substitution with curly braces.
You should create the board in a question variable, so that its state can be saved and restored in case the student leaves and resumes their attempt.
The jxg_ functions described below can be used to access properties of a JSXGraph diagram inside a part's marking algorithm.
To link a part's input field to a JSXGraph diagram, you must describe how the link will work in each direction: from the input to the diagram, and vice versa.
This is achieved by defining two notes in the part's marking algorithm, jxg_input and jxg_output.
jxg_inputThe jxg_input note describes how to take input from the student and modify the JSXGraph diagram.
It is a list of operations to perform.
Since this is a marking note, all the other notes and variables in the algorithm are available.
The available operations are as follows:
jxg_set_position(object,position)Set the position of object to the given position, which should be a 2D vector.
jxg_show(object,visible)If visible = true, then show object, otherwise hide it.
jxg_set(object,property,arguments)Call the JSXGraph set<property> on the given object, with the given arguments.
For example, jxg_set(c, "radius", 5) calls the JSXGraph method c.setRadius(5).
jxg_set_attribute(object,attributes)attributes should be a dictionary mapping attribute names to values.
This calls the JSXGraph method setAttribute on the given object.
jxg_outputThe jxg_output note describes how to take the state of the diagram to fill in the part's input field.
The note should return a value of the same type as the marking algorithm's studentAnswer variable.
For example, for a "number entry" part this note should produce a string, representing what the student should write.
jsxgraph(width, height, [boundingBox], objects, [options])Create a JSXGraph board.
width: number - the displayed width in pixels of the board.height: number - the displayed height in pixels of the board.boundingBox: [number, number, number, number] - an optional list of four numbers defining the bounding box of the diagram, in the format [x1,y1,x2,y2]. The first point is mappted to the top-left of the board, and the second point to the bottom-right.objects: list or dict - a list of object definitions, or a dictionary mapping object names to definitions.options: dict - a dictionary of options for the board. See the documentation for JSXGraph.initBoardAn object definition is a list in the format [type, parents, attribute].
type: string - the type of object to create. Available types are listed in the 'Elements' section of the JSXGraph' documentation.parents: list - a list of 'parent' values for the object. The format of this list depends on the type of the object being created. Any values of data type expression are replaced with functions which take a single parameter.attributes: dict - an optional dictionary of attributes for the object. The available attributes dpeend on the type of the object.events: dict - an optional dictionary of events produced by the object to react to. Each event name should correspond to a string of JessieCode.JSXGraph will often interpret string values in the parents list as JavaScript expressions. You can refer to other objects by name.
Alternately, you can provide JME expression values to evaluate expressions in terms of JME variables and functions. JSXGraph objects are not available in these expressions, and they're quite a bit slower than JavaScript.
This creates a board with a line and a glider point on that line:
jsxgraph(
500,500,
[
"l": ['line', [[0,1], [1,2]]],
"p": ['glider', ['l']]
]
)
This creates a board with a polar plot of the function r(phi) = sin(5phi) centred at the origin:
jsxgraph(
500,500,
[
['curve', [expression('sin(5phi)'),[0,0],0,2pi], ["curveType": "polar"]]
]
)
jessiecode(width, height, [boundingBox], script, [options])Create a JSXGraph board defined by a JessieCode script.
JessieCode is a languaged designed for efficiently creating diagrams in JSXGraph. There is some documentation about JessieCode syntax. The available object types and their construction options are the same as in the JavaScript API.
width: number - the displayed width in pixels of the board.height: number - the displayed height in pixels of the board.boundingBox: [number, number, number, number] - an optional list of four numbers defining the bounding box of the diagram, in the format [x1,y1,x2,y2]. The first point is mappted to the top-left of the board, and the second point to the bottom-right.script: string - a JessieCode script defining the objects in the board. options: dict - a dictionary of options for the board. See the documentation for JSXGraph.initBoardThis creates a board with two points and a dashed line between them, and no background grid or axes:
``` jessiecode( 500,500, """ A = point(0,1); B = point(4,0);
line(A,B) << dash: 4 >>;
""",
[
"axis": false
]
) ```
jxg_add_objects(board, objects)Add a list of objects to the given board.
The format for objects is the same as in the jsxgraph() function. Use this to add extra objects to a board - if you want to produce several similar boards, you might define a function which produces a generic board and then use this to add extra objects.
jxg_run_jessiecode(board, code)Run a JessieCode script, given as a string, on the given board. You could use this to add objects, or modify some aspect of the board.
board[name]Boards created by the jsxgraph and jessiecode functions work like dictionaries: the keys are the IDs of objects in the board.
JSXGraph assigns automatic IDs to objects, but these aren't predictable, so if you want to access an object later you have to explicitly set its id attribute.
For example, board["A"] gets the object with the ID "A".
To access properties of an object in a board, use one of the jxg_ functions.
The following is a brief description of what the jxg_ functions do. Refer to the JSXGraph documentation for more information.
jxg_angle(object)Defined only on lines.
Returns the angle between the line and the x-axis.
jxg_area(object)Defined only on circles and polygons.
Returns the area of the object.
jxg_attribute(object,key)Get the value of an arbitrary attribute of an object.
Example: jxg_attribute(board["A"],"size")
jxg_bounding_box(object)Defined only on polygons.
Returns the coordinates of the smallest box containing the polygon, as a list of four numbers [minX,minY,maxX,maxY].
jxg_bounds(object)Returns the coordinates of the smallest box containing the polygon, as a list of four numbers [minX,minY,maxX,maxY].
jxg_distance(a,b)Defined only on points, text and images.
Returns the distance between two points.
jxg_has_point(object,x,y) or jxg_has_point(object,position)Returns true if the point with the given coordinates is contained in object.
You can either give the coordinates as two separate numbers, or as a 2D vector.
jxg_has_point_sector(object,x,y)Defined only on arcs and sectors.
Returns true if the point with coordinates (x,y) is contained in the sector defined by object.
jxg_length(object)Defined only on lines.
Returns the length of the line.
jxg_max_x(object)Defined only on circles, curves, lines and turtle graphics.
Treating the object as a parametric curve, returns the maximum value of the parameter.
jxg_min_x(object)Defined only on circles, curves, lines and turtle graphics.
Treating the object as a parametric curve, returns the maximum value of the parameter.
jxg_name(object)Returns the name of the object.
jxg_parents(object)Returns a list of the IDs of the object's parents.
jxg_perimeter(object)Defined only on polygons.
Returns the length of the object's perimeter.
jxg_position(object)Returns the object's position as a vector.
jxg_position_at(object,t)Defined only on circles, curves, lines and turtle graphics.
Returns the coordinates of the point at parameter t on the object, as a vector.
jxg_radius(object)Defined only on arcs, sectors and circles.
Returns the radius of the object.
jxg_rise(object)Defined only on lines.
Returns the y-coordinate at which the line crosses the y-axis.
jxg_size(object)Defined only on text objects.
Returns the size of the text.
jxg_slope(object)Defined only on lines.
Returns the gradient of the line, or infinity if it is parallel to the y-axis.
jxg_type(object)Returns the type of the object as a string.
jxg_value(object)The returned value depends on the type of the object:
angle - the angle, as a number in radians.arc - the length of the arc, as a number.input - the content of the input element, as a string.slider - the value the slider is currently set to, as a number.checkbox - whether the checkbox is ticked, as a boolean.tapemeasure - the length of the tape measure.integral - the value of the integral.riemannsum - the sum of the rectangles.jxg_width(object)Defined only on images.
Returns the width of the image, as a number.
All JavaScript functions are properties of the object Numbas.extensions.jsxgraph.
makeBoardPromise(width, height, options)Make a JSXGraph board with the given dimensions and options, and attach it to a <div> element.
The board is only initialised once the container element has been attached to the page.
An object of the format {element, promise} is returned.
The element is a container <div> element that the board will be created under.
The promise resolves to the JSXGraph Board object once the container has been attached to the page and the board initialised.
The options object is passed to JSXGraph.initBoard, with some defaults applied.
The default options are:
{
boundingBox:[-5,5,5,-5],
showCopyright:false,
showNavigation:false,
axis:true
}
This creates a board with the default options and a single point at the coordinates (1,2).
var result = Numbas.extensions.jsxgraph.makeBoardPromise(500,500);
result.promise.then(function(board) {
board.create('point',[1,2]);
});
return result.element;
makeBoard(width, height, options)Make a JSXGraph board with the given dimensions and options, and attach it to a <div> element.
The div element is returned. The JSXGraph board object is available as div.board.
The options object is passed to JSXGraph.initBoard, with some defaults applied.
The default options are the same as for makeBoardPromise.
Note: This function initialises the board immediately, and is kept for backwards compatibility.
It's better to wait until the container element is attached to the page, using makeBoardPromise.
This creates a board with the default options and a single point at the coordinates (1,2).
var div = Numbas.extensions.jsxgraph.makeBoard(500,500);
div.board.create('point',[1,2]);
return div;
Following the paper "the paramagnetic and glass transitions in sudoku"
]]>My 5-minute talk at the big MathsJam conference this weekend was about some stacking cups that my daughter is too young to appreciate. Here’s the really quick version, in just over a minute: …
]]>A five-minute timer for MathsJam Gathering talks.
]]>Find the distance between a 'home' postcode and a list of other postcodes.
Uses data from https://www.getthedata.com/open-postcode-geo
]]>This repository contains Python code that makes the @MathsJam Twitter account automatically retweet local MathsJams on the day of and the day before events.
Before running this code, you will need to:
config.py.template called config.py and fill in the Twitter API keys for the MathsJam account.bash
pip install -r requirements.txtOnce you've done this you can run the code using:
bash
python tweet.py
If you want to test the code without actually sending and tweets, you can do this using:
bash
python tweet.py test
Truchet tiles are square tiles with quarter-circles on opposite vertices. When you put several tiles together, the circles line up. You can extend the idea to any even-sided regular polygon.
There are, as far as I know, only four tilings of the plane with even-sided regular polygons.
This was inspired by Colin Beveridge's article in Chalkdust magazine, Too Good to be Truchet. I haven't actually read the article yet, only the title...
]]>An attempt to simulate people spontaneously forming a Venn diagram
]]>If you see me doing a maths thing, I’m probably wearing one of my maths t-shirts. I’ve got quite a few, but the one that reliably produces the much-sought-after look of total indifferen…
]]>Every rational number can be written as the sum of unit fractions
]]>A never-ending list of reciprocals
]]>A never-ending list of factorisations of the natural numbers.
]]>Shows a big timer counting down from 10 minutes.
]]>A page showing a big HH:MM:SS time display and nothing else
]]>Provides data and functions to make calculations in chemistry easier.
Functions and data are being added as needed.
This has been written by a non-chemist, in collaboration with some non-coding chemists.
atom(symbol, [mass number]) or atom(atomic number, [mass number])Construct an atom of the element with the given symbol or atomic number.
Give mass number to specify a particular isotope.
Example:
atom("Na")atom("Na",19)atom(11)name(atom)The IUPAC name of the atom, with the mass number appended if it's a particular isotope.
Examples:
name(atom("Na")) → "sodium"name(atom("Na",19)) → "sodium-19"symbol(atom)The symbol of the atom.
Example:
symbol(atom(11)) → "Na"string(atom)A plaintext string representation of the atom. For isotopes, the mass number is shown in superscript.
Example:
string(atom("Na",19)) → "¹⁹Na"atom[property]Get the given property of the atom, from the periodic_table_data or isotope_data dictionaries.
Examples:
atom("Na")["electronegativity"] → 0.93atom("C",13)["Isotopic Composition"] → "0.0107(8)"atomic_number(atom)The atomic number of the atom.
Example:
atomic_number(atom("C")) → 6mass_number(atom)The mass number of the given atom. If no isotope is specified, the most abundant isotope is used.
Example:
mass_number(atom("Na")) → 23neutrons(atom)The number of neutrons in the atom Equivalent to mass_number(atom) - atomic_number(atom).
Example:
neutrons(atom("Na")) → 12relative_mass(atom)The relative atomic mass of the atom. If no isotope is specified, the standard atomic weight is used.
Example:
relative_mass(atom("C")) → 12.0107isotopes(atom)List the mass number of known isotopes of the element.
Example:
isotopes(atom("H")) → [ 1, 2, 3, 4, 5, 6, 7 ]abundance(atom)Abundance of the given isotope. If no isotope specified, returns 1.
Example:
abundance(atom("C",13)) → 0.0107formula(string)Construct a compound from the given formula.
Examples:
formula("H2O")formula("C(CH3)4")formula("2(N2)")string(formula)A plain text display representation of the given formula.
Example:
string(formula("H2O")) → "H₂O"plain_string(formula)A plain text display representation of the given formula.
Example:
string(formula("H2O")) → "H₂O"atom_counts(formula)Returns a dictionary containing the number of atoms of each element present in the compound.
Example:
atom_counts(formula("C(CH3)4")) → [ "C": 5, "H": 12 ]mass(formula)The relative atomic mass of the compound specified by the formula.
Example:
mass(formula("C(CH3)4")) → 72.14878thermodynamic_data(name,state)Returns data from the thermodynamic_data dictionary for the chemical with the given name or symbol, in the given state.
Common values of state are "g" (gas), "l" (liquid), "c" (crystalline), or "amorphous". Some other states are also listed; see thermodynamic_data.
Some data sets are available as variables.
periodic_tableBased on https://github.com/andrejewski/periodic-table, which is itself based on https://web.archive.org/web/20161203095654/http://php.scripts.psu.edu/djh300/cmpsc221/p3s11-pt-data.htm.
A list of dictionaries providing data on elements in the periodic table.
periodic_table[n] give data for the element with atomic number n.
Each dictionary has the following keys:
atomicNumbersymbolnameatomicMass - Standard atomic weight, with precision (e.g. "1.00794(4)")cpkHexColor - Hex representation of the element's CPK colourelectronicConfiguration - Electron configurationelectronegativity - Pauling electronegativityatomicRadius - Atomic radius in pmionRadius - Ion radius in pmvanDerWaalsRadius - van der Waals radius in pmionizationEnergy - IE-1 in kJ/molelectronAffinity - EA in kJ/moloxidationStates - Oxidation statesstandardState - Standard state, one of "gas", "solid", "liquid" or ""bondingType - Bonding type, one of "diatomic", "atomic", "metallic", "covalent network" or ""meltingPoint - Melting point in KboilingPoint - Boiling point in Kdensity - Density in g/mLgroupBlock - Group, one of "nonmetal", "noble gas", "alkali metal", "alkaline earth metal", "metalloid", "halogen", "metal", "transition metal", "lanthanoid", "actinoid", "post-transition metal"yearDiscoveredisotope_dataBased on https://www.nist.gov/pml/atomic-weights-and-isotopic-compositions-relative-atomic-masses
Data on properties of isotopes. A dictionary of the form {atomic_number: {mass_number: data}}.
Each dictionary has the following keys:
"Isotopic Composition" - Abundance of this isotope as a proportion between 0 and 1, with precision (e.g. "0.524(1)")"Notes" - See the NIST column description"Standard Atomic Weight" - Standard atomic weight of the element, with precision"Atomic Number""Atomic Symbol""Relative Atomic Mass" - Relative atomic mass of this isotope"Mass Numberthermodynamic_dataBased on Newcastle University's thermodynamic data card.
Data on thermodynamic properties of some chemicals in different states. Most easily accessed with thermodynamic_data(name,state).
A list of dictionaries with the following keys:
"name""formula""state" "Hfg" - Enthalpy of formation, in kJ/mol"Gfg" - Gibbs free energy, in kJ/mol"Smg" - Entropy, in J/mol/K"Cpm" - Specific heat capacity, in J/mol/KA tool to decide who goes first: everyone loads this on their phone, and presses the screen at the same time. The phones will all ping at different times. A knock-off of an app I saw someone use. Contains a QR code so everyone else can load it easily.
]]>This extension wraps the js-quantities library to provide a "quantity with units" data type to Numbas.
It provides a quantity data type, which represents a scalar amount and a list of units.
A note about precision: Amounts are represented with JavaScript floating-point numbers, which are only precise to around 30 decimal places. It's our intention to add support for more precise number representations to Numbas; when that happens, this extension could be updated to use that.
quantity([number], units) or qty([number], units)Create a quantity with the given units. If the number is not given, the returned quantity represents 1 of the given units.
Use * and / to combine units, and ^ for powers of units.
An empty units string will produce a unitless quantity.
Examples:
quantity("kg")qty("kg")quantity(1.2, "kg")quantity("kg*m/s^2")quantity("1/s")quantity("kg*m^-2")units_of_kind(kind)Returns a list of recognised units of the given kind.
Example:
units_of_kind("length") → [ "angstrom", "AU", "datamile", "fathom", "foot", "furlong", "inch", "league", "light-minute", "light-second", "light-year", "meter", "mil", "mile", "naut-mile", "parsec", "pica", "point", "redshift", "rod", "yard", "m" ]aliases(unit)Returns a list of accepted names for the given unit.
Example:
aliases("meter") → [ "m", "meter", "meters", "metre", "metres" ]compatible(q1, q2)Are the two given quantities compatible? That is, are they of the same kindm, so one can be converted to the other?
Examples:
compatible(qty("m"), qty("ft")) → truecompatible(qty("m"), qty("kg")) → falsekind(quantity)What kind of unit is quantity measured in?
Returns a string code corresponding to the kind.
For combinations of units that don't correspond to a built-in kind, an empty string is returned.
Examples:
kind(qty("m")) → "length"kind(qty("N*s")) → "momentum"kind(qty("W/s")) → ""unitless(quantity)Does the given quantity have no associated units? Note that a dimensionless quantity is not necessarily unitless - for example, a quantity measured in meters per foot has no dimension but is not unitless.
unitless(qty(1,"")) → trueunitless(qty(1,"m")) → falseunitless(qty(1,"m/ft")) → falseisbase(quantity)Is the given quantity in base units, as defined in the International System of Units (SI)?
Examples:
isbase(qty("kg")) → trueisbase(qty("lb")) → falsetobase(quantity)Convert the given quantity to base SI units.
Example:
tobase(qty(1, "inch")) → quantity(0.0254, "m")q_from in units or q_from in q_toConvert quantity q_from to the units specified by the given string, or the same units as q_to.
If the desired units are not compatible with q_from, an error is thrown.
Examples:
qty(1.5, "m") in "cm" → quantity(150, "cm")qty(100,"g") in qty("kg") → quantity(0.1, "kg")as_si(quantity)Convert quantity to SI units.
Example:
as_si(quantity(1, "lb*ft")) → quantity(0.1382549544, "m*kg")inverse(quantity)Return the reciprocal of the given quantity.
Example:
inverse(qty(2, "m/s")) → quantity(0.5, "s/m")same(a, b)Are a and b both exactly the same quantity, measured in the same units?
Examples:
same(qty(1, "m"), qty(100, "cm")) → falsesame(qty(1, "m"), qty(2, "m")) → falsesame(qty(1, "m"), qty(1, "m")) → truea < b, a <= b, a > b, a >= bCompare quantities a and b. If their units are not compatible, an error is thrown.
Examples:
qty(1, "cm") < qty(1, "m") → trueqty(4, "feet") > qty(1, "m") → trueqty(1, "cm") > qty(1, "cm") → falseqty(1, "cm") >= qty(1, "cm") → truea = bQuantities a and b are equal if their units are compatible and they represent exactly the same amount.
Examples:
qty(2.54, "cm") = qty(1, "inch") → trueqty(1, "cm") = qty(1, "second") → falsea + b, a - b, a * b, a / bArithmetic on units is supported. When adding or subtracting units, the result is given in the same units as the left-hand argument, and if the two quantities being combined are in incompatible units an error is thrown.
When multiplying or dividing, units are not automatically converted to their common names. For example, The division of a quantity in Newtons by a quantity in m^2 returns a quantity in N/m^2, not in Pa.
Examples:
qty(1, "cm") + qty(1, "m") → quantity(101, "cm")qty(100, "cm") - qty(1, "m") → quantity(0, "cm")qty(100, "cm") * qty(1, "s") → quantity(100, "cm*s")qty(100, "N") / qty(4, "m^2") → quantity(25, "N/m^2")-qThe negative of the given quantity.
Example:
-qty(1,"N") → quantity(-1, "N")n * quantity or quantity * nMultiply a quantity by a scalar.
Example:
5 * quantity(6, "g") → quantity(30, "g")round(quantity, precision)Round quantity to the nearest multiple of precision, given either as a string in the form "amount units", or another quantity.
If precision is not given, the quantity is rounded to the nearest whole unit.
Examples:
round(qty(123, "cm"), "1 m") → quantity(100, "cm")round(qty(0.1697, "m"), qty(5, "cm")) → quantity(0.15, "m")round(qty(6.32, "kg")) → quantity(6, "kg")precround(quantity,dp)Round quantity to dp decimal places.
Example:
precround(qty(123.456,"cm"), 1) → quantity(123.5, "cm")siground(quantity,sf)Round quantity to sf significant figures.
Example:
siground(qty(123.456,"cm"), 1) → quantity(100, "cm")abs(quantity)The absolute value of the quantity.
Example:
abs(qty(-5, "N")) → quantity(5, "N")scalar(quantity)The scalar amount of the quantity, as a number - in other words, strip off the units information.
Example:
scalar(qty(53, "s")) → 53sign(quantity)Returns 1 if the quantity is positive, -1 if it's negative, and 0 otherwise.
Examples:
sign(qty(20,"kg")) → 1sign(qty(-10,"kg")) → -1sign(qty(0,"kg")) → 0string(quantity, [notation style])A string representing the given quantity, in the given notational style (plain English is the default) The units are presented using "nice" characters: a dot for multiplication, and exponents are displayed in superscript.
Examples:
string(qty(23, "kg*s^-1")) → "23 kg/s"string(qty(1000.235, "kg*m^2"), "si-fr") → "1 000,235 kg⋅m²"plain_string(quantity, [notation style])A string representing the given quantity, in the given notational style (plain English is the default).
The units are given in a form that is easy to type: * is used for multiplication, and ^ marks an exponent.
Examples:
plain_string(qty(23, "kg/s")) → "23 kg/s"plain_string(qty(1000.235, "kg*m^2"), "si-fr") → "1 000,235 kg*m^2"units_numerator(quantity) and units_denominator(quantity)The units of a quantity can be written as a fraction whose numerator and denominator both consist of a list of units. When a squared or cubed unit is used, the base unit is repeated. These functions return the lists of units in the numerator and denominator, respectively, for a given quantity. Any order-of-magnitude prefixes are included as separate items in the list
Examples:
units_numerator(qty(1, "m")) → [ "meter" ]units_denominator(qty(1, "m")) → [ "1" ]units_numerator(qty(1, "kg*cm^2/s^2")) → [ "kilogram", "centi", "meter", "centi", "meter" ]units_denominator(qty(1, "m^2/s^2")) → [ "second", "second" ]units(quantity)Return a quantity representing one unit of the same kind as the given quantity.
Example:
units(qty(23, "m^2/s")) → quantity(1, "m^2/s")units_string(quantity)Return a string describing the units of the given quantity, suitable for display. Powers are displayed in superscript, and ⋅ is used for multiplication. The units are presented using "nice" characters: a dot for multiplication, and exponents are displayed in superscript.
Example:
units_string(qty("kg*cm^2/s^2")) → "kg⋅cm²/s²"plain_units_string(quantity)Return a string describing the units of the given quantity, suitable for display. Powers are displayed in superscript, and ⋅ is used for multiplication.
The units are given in a form that is easy to type: * is used for multiplication, and ^ marks an exponent.
Example:
plain_units_string(qty("kg*cm^2/s^2")) → "kg*cm^2/s^2"quantity_kindsA constant list of all the recognised kinds of units.
See the documentation for js-quantities for detail on its JavaScript API.
The Qty object is available globally when this extension is loaded.
In addition, the following functions are defined under the Numbas.extensions.quantities namespace:
precround(q,dp)Round the quantity q to dp decimal places. See Numbas.math.precround
siground(q,sf)Round the quantity q to sf significant figures. See Numbas.math.siground
The following unit names are recognised. Units of the same kind can be converted between each other. The prefixes can be written to the left of any other unit, to change the order of magnitude.
Randomly generate the names of statistically plausible people
]]>Show a diagram to compute divisibility by any number
]]>A competition to find The World's Most Interesting Mathematician. It's really a way of tricking people into telling me fun maths.
]]>I think the gist of the game would be that in each round, everyone guesses a number, and at the end the closest guess to the average of everyone's guesses wins. It didn't really work.
]]>Spinny squares
]]>Based on a trick I saw David Bedford perform.
]]>Show every fraction in the range [0,1] on a polar plot, using the Calkin-Wilf sequence.
Inspired by @[email protected]'s toot.
]]>Demonstrating the incredible fact that every integer can be written as the sum of 3 palindromes.
]]>A thing to help Katie Steckles decode the secret message in her birthday card.
]]>A never ending series of sums, where the answer from the last question is part of the next question.
]]>An interactive version of Puzzle 403 from Henry Ernest Dudeney's Amusements in Mathematics.
]]>JavaScript library to convert AsciiMath to TeX.
I wrote this so I could use KaTeX to render AsciiMath.
The library is written as an ECMAScript module but is also distributed as CommonJS and UMD modules.
The file asciimath2tex.js is an ES6 module which can be used in browsers that support it.
If you can use ES6 modules, use asciimath2tex.js as-is:
```js import AsciiMathParser from 'asciimath2tex';
const parser = new AsciiMathParser(); const tex = parser.parse("int_(i=1)^10 x^2/2 dx"); ```
parser.parse returns a string of TeX.
If you can't use ES6 modules, there is a UMD version. You can load it from unpkg.com, or create it yourself.
```html
```
Clone this repository, and run:
npm install
npm run-script build
Copy the file dist/asciimath2tex.umd.js into your project.
Install the package using npm:
npm install asciimath2tex
You can then load the package in your script:
```js const AsciiMathParser = require('asciimath2tex');
const parser = new AsciiMathParser(); const tex = parser.parse("int_(i=1)^10 x^2/2 dx"); ```
There are some unit tests in test.html, copied from the asciimath repository.
All of the tests are rendered correctly by KaTeX, apart from \twoheadrightarrowtail, which it apparently doesn't support.
A one-dimensional "lights out" variant, played on a circle. You can toggle the state of a bulb, as long as you also toggle every d^th bulb after it, where d is a divisor of the total number of bulbs.
You start with one light turned on. Can you turn all the lights out?
]]>A lights-out game, with N states instead of just 2.
The URL of the page always represents the state of the game, so you can link to a particular state by copying the URL.
]]>Exploding Dots is James Tanton's thing.
It just keeps counting.
I've used some ES2015 features, so to make it work in older browsers you need to use BabelJS.
Install babel with
npm install
and then run
babel thing.js -o thing.compiled.js
A Numbas extension providing a collection of functions to generate random people, for use in word problems.
Demo: https://numbas.mathcentre.ac.uk/question/23094/the-random-person-extension/
It doesn't really matter what people are called in word problems, but it can have a bad effect on students' perceptions of the world if the plumber's always called Gary and the nurse is always called Julie.
An easy fix is to flip a coin each time you need a name, and choose a male name if it's heads, and a female name if it's tails.
But names come with much more baggage than gender! Social class, age and cultural heritage are just a few of the things you can have a stab at guessing based on someone's name.
So, this extension makes it really easy to randomly pick a name for a representative citizen of England and Wales born between 1996 and 2015, using the ONS's dataset of baby name frequencies in England and Wales, 1996 to 2015.
Names which were given to more than 100 each of males and females are classed as gender-neutral.
Additionally, when picking names without specifiying a gender, occasionally a name that would be returned as "male" or "female" is instead returned as "neutral", representing a person who identified as non-binary.
The proportion of the time that this happens is controlled by the JavaScript variable Numbas.extensions.random_person.PROB_NONBINARY, which is 1/100 by default.
The extension contains some built-in JavaScript objects with the data for name frequencies, and pronouns associated with each gender. You can replace these objects with your own data.
There are some other datasets in Numbas.extensions.random_person.datasets.
To switch to another dataset, put something like this in your question's Javascript preamble:
var random_person = Numbas.extensions.random_person;
random_person.data = random_person.datasets.fr;
The name frequencies are stored in an object at Numbas.extensions.random_person.data.
This object has the form:
{
"names": { gender: list of objects {"name": string, "count": integer} },
"totals": { gender: integer }
}
(gender stands for a key representing each gender. In the built-in data set, these are "male", "female" and "neutral".)
There is also an object giving pronouns for each gender, stored at Numbas.extensions.random_person.pronouns.
This object has the form:
{ gender: pronoun_map }
The built-in pronoun_map is an object mapping each of the strings "they", "their", "theirs", "them", "themself" to the corresponding pronouns for that gender.
Each of these keys is added to the object returned for a randomly-generated person, so if you are using a language other than English, you could provide a different set of keys.
they(person), equivalent to person['pronouns']['they'].The name frequency data is Crown Copyright, reproduced under the Open Government Licence.
The rest of this package is released under the terms of the Apache License 2.0. See the LICENSE file for more information.
The code returns dictionaries representing people, of the form
{
"gender": string, //("male", "female", or "neutral" at the moment)
"name": string,
"pronouns": {
"they": the subjective personal pronoun, e.g. 'he' or 'she',
"their": the possessive determiner, e.g. 'his' or 'her',
"theirs": the possessive pronoun, e.g. 'his' or 'hers',
"them": the objective personal pronoun, e.g. 'him' or 'her',
"themself": the reflexive pronoun, e.g. 'himself' or 'herself'
}
}
Generate a random person, or people, using the JME functions described below.. Each time you need to use their name, or a pronoun referring to them, use the corresponding entry from their dictionary, as described above.
Here's an example:
``` {person['name']} puts {person['pronouns']['their']} things where {person['pronouns']['they']} like{if(person['gender']='neutral','','s')}.
When people show things to {person['pronouns']['them']}, {person['pronouns']['they']} want{if(person['gender']='neutral','','s')} them for {person['pronouns']['themself']}. ```
The singular "they" is used for gender-neutral people. Don't forget that verb conjugation is different for singular "they": for example, "Charlie likes to read while they walk" compared to "Charlie likes to read while he walks".
If you've only got one person, it can be more convenient to set variables for 'they', 'their', etc. and for verb conjugation, so you don't have to type person['pronouns']['their'] each time.
Here's that example again:
``` {name} put{s} {their} things where {they} like{s}.
When people show things to {them}, {they} want{s} them for {themself}. ```
If you need more than one person, use random_people to ensure that you don't have any repeated names, which could lead to confusion.
random_person()A person with random name and gender.
random_person_with_gender(gender)A person with random name and the given gender.
random_people(n)n unique people with random names and genders. If you need more than one person in your question, use this to make sure that no names are repeated.
random_people_with_gender(gender,n)n unique people with random names and the given gender.
random_person_with_initial(letter)A person with random name and gender, whose name starts with the given letter.
random_people_with_different_initials(n)n unique people with random names, whose names each start with distinct letters.
Way back at the end of last year I put out a call to mathematicians I know: hop on Skype and chat to me for a while about the work you’re doing at the moment. The first person to answer was D…
]]>A collection of maths jokes and explanations of why they're funny.
]]>This extension provides a few functions which make working with linear algebra easier.
The three kinds of permitted row operation are:
k times one row from another.This extension works with matrices over the rationals: before any operations are performed, the matrix's cells are converted to fractions.
row_echelon_form(matrix)Uses row operations to put the given matrix in row echelon form. A matrix is in row echelon form if the leading non-zero entry in each row is strictly to the right of the leading non-zero entries in all of the rows above.
row_echelon_form_display(matrix)Returns a passage of HTML describing the steps involved in transforming the given matrix into row echelon form.
row_echelon_form_display_determinant(matrix)Returns a passage of HTML describing the steps involved in transforming the given matrix into row echelon form, while describing how the determinant of the matrix changes at each step.
is_row_echelon_form(matrix)Returns true if the matrix is in row echelon form.
describe_why_row_echelon_form(matrix)Returns a string describing why the matrix is not in row echelon form, or "The matrix is in row echelon form." if it is.
reduced_row_echelon_form(matrix)Uses row operations to put the given matrix in reduced row echelon form. A matrix in row echelon form is reduced if every leading non-zero entry is 1 and is the only non-zero entry in its column.
reduced_row_echelon_form_display(matrix)Returns a passage of HTML describing the steps involved in transforming the given matrix into reduced row echelon form.
is_reduced_row_echelon_form(matrix)Returns true if the matrix is in reduced row echelon form.
describe_why_reduced_row_echelon_form(matrix)Returns a string describing why the matrix is not in reduced row echelon form, or "The matrix is in reduced row echelon form." if it is.
rank(matrix)Computes the rank of the matrix, by returning the number of rows of the reduced row echelon form of the matrix, with all zero rows removed.
is_linearly_independent(matrix)Returns true if the rows of the matrix are linearly independent.
adjoin(matrix,vector)Add the given column vector to the right of the given matrix.
subset_with_dimension(vectors,n,d)Return a subset of n of the given vectors, with dimension d.
This is not always possible - if the vectors have length k, you can't have d>k.
This is not If the input list has dimension less than d, it can't be done.
This is not Likewise with extra dependent vectors - if there aren't enough, it'll fail.
The vectors are processed in order, so if you want a random subset you should shuffle the list first.
as_sum_of_basis(basis,v)Express the vector v as the sum of the given list of basis vectors.
Returns a list of coefficients corresponding to the basis vectors.
All JavaScript functions are members of the Numbas.extensions.linearalgebra object.
Fraction(n)A constructor for a fraction. n is either a number, in which case a rational approximation is computed, or an object {n: numerator, d:denominator}.
Example:
var two = new Fraction(2);
var third = new Fraction(1/3);
var two_thirds = two.mul(third);
fraction_matrix(matrix)Convert all the entries in the given matrix to Fraction objects. Returns a new matrix - doesn't modify the original.
unfraction_matrix(matrix)Convert all the entries in the given matrix from Fraction objects to normal numbers. Returns a new matrix - doesn't modify the original.
row_echelon_form(matrix)Transforms the given matrix into row echelon form. Returns an object {matrix: matrix, operations: [list of descriptions of the steps]}.
reduced_row_echelon_form(matrix)Transforms the given matrix into reduced row echelon form. Returns an object {matrix: matrix, operations: [list of descriptions of the steps]}.
is_row_echelon_form(matrix)Returns true if the given matrix is in row echelon form. If not, throws an error whose message is an explanation of why the matrix isn't in row echelon form.
is_reduced_row_echelon_form(matrix)Returns true if the given matrix is in reduced row echelon form. If not, throws an error whose message is an explanation of why the matrix isn't in reduced row echelon form.
rank(matrix)Returns the rank of the given matrix.
is_linearly_independent(vectors)Returns true if the given list of vectors is linearly independent.
adjoin(matrix,vector)Add the given column vector to the right of the given matrix.
subset_with_dimension(vectors,n,d)Returns a subset of n of the given vectors with dimension d.
as_sum_of_basis(basis,v)Express the vector v as the sum of the given basis vectors.
Returns a list of coefficients corresponding to the basis vectors.
Makes a printable template for a hexaflexagon containing a picture from your camera, or any image you upload. Made for outreach purposes at work.
]]>This repository contains the source files to generate the MathsJam website.
Edit this file button (it's a little pencil icon in the top right). The files for individual Jams are in the cities folder. Alternatively, each page on the site has an "edit this page" link at the bottom; clicking this will take you to the corresponding GitHub page.Information about each city is stored in a file in the cities folder.
Other pages are generated from .md files in the repository. The front page is generated from index.md.
Create a file cities/cityname.md, following this template:
layout: city
city_name: CityName
local_jam_type: MathsJam (for jams in the US this is "MathJam")
jam_name: CityName MathsJam
email: [email protected]
twitter: CityNameMathsJam
facebook: https://www.facebook.com/groups/000000000000/
organiser:
name: Organiser's name
email: [email protected]
location:
group: england/rest-of-uk/north-america/rest-of-world
pub_name: "Ye Pub"
description: " on X Street"
url: http://www.yepub.website
lon: 1.00000000 (get the lat and long from google maps)
lat: 50.0000000
hiatus: False (change to True if the Jam is not currently running)
hiatus_months: (add this field if the Jam doesn't run for one or two months)
- 2016-01
- 2016-02
changed_dates: (add this field if the Jam is running on a different day one month - give the date the Jam will happen)
- 2015-03-23
- 2016-09-28
jam_date_rule: second-last Tuesday
december_jam_date_rule: third-last Tuesday
start_time: 7pm in the evening
lang: language code (for British jams, this is "en_GB")
poster_text: |
Some custom body text
for the poster
(Only if you want)
Maximum seven lines.
```
All the data from this file will be used to create the page for the Jam, and all other references to the city throughout the site.
If your Jam doesn't meet on the second-last Tuesday of the month, you can add a line for 'jam_date_rule' and follow the convention of 'first/second/third/fourth(-last) Weekday', e.g. fourth Thursday, third-last Saturday. If you can't work out how to word it, do your best and we can fix it before merging.
Most Jams run on a different date in December to avoid Christmas, so there's a separate december_jam_date_rule line, with a default of 'third-last Tuesday'.
If you need to put your MathsJam on hiatus for a single month, this is also possible - add the 'hiatus_months' field, and then on successive lines add the months for which you won't be meeting, in the format YYYY-MM, as in the example above. This will automatically add a note to the site and remove it again afterwards. A similar procedure is available using the 'changed_dates' field for if your MathsJam is running on a different date for one particular month.
If you want your poster to be in a different language, set the lang and poster_text fields.
A thing to tot up everyone's ratings while watching Eurovision.
]]>Requires Python 3.
tootbib register --mastodon <URL of your Mastodon instance>tootbib login --mastodon <URL of your Mastodon instance> --email <your Mastodon account's email address> --password <your Mastodon account's password>tootbib --api_base_url <URL of your Mastodon instance> --bibsite <URL of your bib-site instance> will pick a random entry and create a toot.
--order <piece names separated by spaces> - specify the order in which the pieces of the toot should be assembled. Default is "title author collections abstract url pdf view"--appfile <filename> - path to the file in which to store the token for your Mastodon app.--userfile <filename> - path to the file in which to store the access token for your Mastodon account.My wife’s school recently sent round a form with questions about “a day in the life” of people working in STEM careers, to show to their year 6 children. My job involves the M in …
]]>A marking algorithm is a set of notes.
A note is a name, an optional description, and a list of procedures.
Applying a procedure returns a JME value or an operation on the state.
The state is a stack of credit/validation/feedback instructions. The only operation is to append instructions to the stack.
Evaluating a note consists of applying all of its procedures, and storing the final JME value and state.
Execution order of the notes is determined by dependency.
(Some context: 'JME' is the name for the scripting language used by the e-assessment system Numbas. Here's a reference for the JME language.)
The following notes are required:
mark - The state at the end of this note will be used to find the student's final score and feedback.as_jme - The student's answer, as a JME value which can be used in adaptive marking or by other parts.These operations are implemented as functions which return a list of state instruction tokens.
set_credit(n,message) - Set the credit to n, and add a feedback message explaining whymultiply_credit(n,message) - Multiply the credit by n, and add a feedback message explaining whyadd_credit(n,message) - Add n to the credit, and add a feedback message explaining whysub_credit(n,message) - Subtract n from the credit, and add a feedback message explaining whycorrect(message) - Set the credit to 1, and add a feedback message explaining whyincorrect(message) - Set the credit to 0, and add a feedback message explaining whyend - End the evaluation here, and don't continue evaluating any other notes which depend on this onefail(message) - End the evaluation here, set credit to 0, mark the student's answer as invalid, and add a feedback message explaining whyassert(test, otherwise) - If test is false, apply otherwisewarn(message) - Display a warning next to the input boxget_answer(part) - Get the student's answer to the given part. The reference can be relative, e.g. g0 will get this part's first gap, while p0g0 will get the first gap in the first part in the question.mark_part(part) - Apply the given part's marking algorithm, and return its final state.feedback(message) - Give the student some feedbackapply(state, [title]) - Append the given state (from another note, for example) to the current state. If a title is given, all the applied feedback messages will be grouped under that title.The following variables are available at any point:
studentAnswer - the student's answer, exactly as they entered itsettings[name] - get the part setting with the given name, e.g. correctAnswer, maxLength.A site to show off interactions in Jupyter notebooks, which were new at the time.
This was built during a hackathon session at the Computational Mathematics with Jupyter Workshop in Edinburgh, with Mike Croucher and a few others.
]]>Render a WordPress blog as static files
At the moment, it creates files which can be used by Spress to create the static pages. Eventually I want to get it to trigger Spress each time it updates.
]]>Build up a plot of the Riemann zeta function by spray paint.
]]>Clever Hans was a famous horse who could answer maths questions. I made my own.
]]>The longer term goal of this work is to use the resulting parser in an online assessment system with students.
npm install nearleymake jme-nearley.jsnumbro.js provides functions to format numbers and currency amounts.
It uses its own format specification language, which is defined by example. See the numbro.js docs for examples of what's possible.
At the moment, numbro keeps the language setting in a global variable, which makes it difficult to provide formatting functions which take a language/culture parameter without affecting other calls to numbro.
To set the language/culture for a question, call numbro.culture in the question preamble, e.g. numbro.culture('nb-NO').
There's a question showing how to use this extension at https://numbas.mathcentre.ac.uk/question/15000/format-numbers-and-currency-amounts-with-numbro-js/
format(n,format)Format the number n following the given format.
Example: format(1234.5,'0,0.00') → "1,234.50"
formatcurrency(n,format)Format n as a currency amount, following the given format.
Example: formatcurrency(12345,'0a') → $12k (when the language is the default value, en-US)
formatcurrency(n,format,currencySymbol)Format n as a currency amount, following the given format and with the given currency symbol.
Example: formatcurrency(12345,'0a','£') → £12k
numbro.js is © 2014 Adam Draper, © 2015 Företagsplatsen AB, and released under the MIT licence.
This extension is © 2016 Newcastle University.
]]>Interactive versions of the Puzzlebomb puzzle sheets
]]>An extension for Numbas which integrates GeoGebra materials.
To use this extension in a question, tick the "GeoGebra" option on the Extensions and Scripts tab.
All the GeoGebra resources are loaded from geogebra.org, so this extension WILL NOT work offline, or in environments where that domain is blocked or otherwise inaccessible.
Warning: At the moment, there's no way for this code to detect when the GeoGebra embedding fails, either due to an incorrect ID or a network issue. If you just get "geogebra applet loading..." for a long time, check your browser's console.
geogebra_applet(id or dimensions, [object definitions], [object-part links]) → ggbappletLoad a GeoGebra material or create a blank worksheet, and optionally modify or create some objects and set up links between objects and question parts
The parameters begin with either the ID of a material to load from geogebra.org, or a pair of numbers representing the width and height of a blank applet worksheet.
The material ID is or the URL of the material, such as https://www.geogebra.org/m/jJ3zQ29z or http://ggbm.at/jJ3zQ29z, or just the random-looking bit, e.g. jJ3zQ29z.
The next parameter is an optional dictionary of definitions (or re-definitions) of objects. The definition can be a value on its own, or a dictionary of properties to set.
If you give a dictionary of properties, the following properties are understood:
definition (a value for the object, or a GeoGebra command)captioncolor (any valid CSS color is accepted)visiblelabel_visiblelabel_stylefixedtracerenamelayerline_styleline_thicknesspoint_stylepoint_sizedisplay_stylefillingThe definition can be:
If the object with the given name is already defined in the applet, then it is updated with the new definition you give. So you can set up your whole worksheet in the GeoGebra editor with placeholder values, and then replace them with the values generated by your question when it runs.
The optional final parameter is a list of links between GeoGebra objects and Numbas parts, in the format [object or exercise name, part id]. When the object changes in GeoGebra, the answer input for the corresponding part is updated, and vice versa.
Be careful about linking dependent objects to part answers: if the student changes the part's answer input, the GeoGebra worksheet can't be updated to reflect that. So only link independent objects to part answers. If you want to use the position of a dependent object in the marking of a part, use an 'extension' type part and the functions detailed below to get the part's position in a custom marking algorithm.
The object returned by this function can be embedded in any content area just like an HTML value.
geogebra_applet('https://www.geogebra.org/m/jJ3zQ29z',[A: vector(ax,ay), B: vector(bx,by), C: vector(cx,cy)])
Loads the given worksheet, and moves points A,B and C to the given positions.
geogebra_applet(800,500,[A: ["definition": vector(1,0), "color": red"]], [["A","p0"]])
Creates a blank worksheet with size 800 by 500 pixels, adds an object A at (1,0) and links the position of A with the answer to the first part in the question.
geogebra_file(filename, [object definitions], [object-part links]) → ggbappletLoad a GeoGebra file from the given file.
The filename parameter can be the name of a resource attached to the question, or the URL of any .ggb file.
The 'object definitions' and 'object-part links' parameters work the same way as for geogebra_applet.
geogebra_base64(base64, width, height, [object definitions], [object-part links]) → ggbappletCreate a GeoGebra applet from the given base64-encoded .ggb file, with the given width and height in pixels.
If you have the base64-encoded version of a .ggb file, this function will create a GeoGebra applet with the given dimensions and load the given worksheet in it.
One way of obtaining the base 64 string for a GeoGebra applet is to run ggbApplet.getBase64() on a page containing the applet. Note: the variable won't always be called ggbApplet - on geogebra.org, for example, a unique string of digits is appended to the variable name.
The 'object definitions' and 'object-part links' parameters work the same way as for geogebra_applet.
Warning: Because of the way GeoGebra loads, these functions only work once the applet has been displayed. That means they don't work during question variable generation. The intended use for these functions is in part marking algorithms.
The following functions get properties of a named object from a GeoGebra applet created with one of the functions above. They are all have the calling signature property(app,object name), e.g. value(app,"A").
value - a representation of the object's value.x - the X coordinatey - the Y coordinatez - the Z coordinatecolor - the color of the object, as a hex string (e.g. #4D4DFF)visible - is the object visible?value_string - GeoGebra's string representation of the object.definition_string - the description of the object.command_string - the command of the object.latex_string - a string of LaTeX representing the object.type - the type of the object, e.g. "point", "numeric".exists - does the object exist in the applet?defined - is the object's value valid at the moment?layer - the layer the object is in.line_styleline_thicknesspoint_stylepoint_sizecaptionlabel_styleThis marking algorithm marks the part as correct if the point A is positioned at the coordinates (1,2).
The GeoGebra applet has been defined in the variable app.
``` a_pos (The position of the object A, as a vector): value(app,"A")
mark: feedback("A is at $\var{latex(a_pos)}$"); correctif(a_pos=target_position)
interpreted_answer: a_pos ```
createGeogebraApplet(options,replacements,parts,question)options is a dictionary of GeoGebra applet parameters. This function returns a Promise object which resolves to an object {app: <GeoGebra applet>, element: <HTML element containing the applet>}.
You could use this function to load an applet and then manipulate it with the GeoGebra JavaScript API before embedding it in the page.
Note that because this function returns a promise, the applet will not have finished loading at the time the question is displayed to the student. You'll have to insert the applet in the page yourself once the promise resolves.
tokToGeoGebra(token)Convert a Numbas JME token to a GeoGebra command.
geoGebraToTok(app,name)Get a JME token representing the value of a GeoGebra object.
app is a GGBApplet object, and name is the name of the GeoGebra object to convert.
Objects of type 'point' are converted to vectors, 'numeric' to numbers, and everything else to strings.
]]>A thing to play the Chaos game on a triangle, making the Sierpiński triangle.
]]>Because What3Words is stupid, here's a thing that converts a lat/long into 4 emoji, and back. It doesn't do anything clever with projections or tilings - it just scales lat and longs linearly onto a 2×(766²)×(766²) grid, and maps that onto four emoji, picked from the set of 912 that emojiOne can render without any combining characters.
The encoding is completely dependent on the set of emoji I chose, and their order - I think they're in ascending Unicode order.
]]>A toy quiz system written in Elm.
I'm just playing about with the Elm language. I thought a little quiz would be a good project to experiment with.
At the moment, the Elm standard library (and even the community packages) don't offer much, so this implements some very basic things.
To build it, run elm-make quiz.elm --output=quiz.js, then open index.html.
A game based on Katie Steckles's binary nail-painting puzzle. The aim is to add or remove varnish from your nails in a sequence such that every combination of painted/unpainted appears on your hand once, while minimising the number of times you remove paint from a finger (acetone isn't free).
Drag rows of the sequence to reorder them. Your best score is shown in red; click it to restore the sequence that made that score.
]]>Even if you don't need that, you might like bib-parse.php, which does a better job of parsing .bib files (or at least, my .bib files) than any other PHP parser I found.
I use this to run read.somethingorotherwhatever.com
]]>This site hosts my list of interesting and unusual papers that I have collected over the years. Many of the references are kept here so I can easily find them again when I want to tell someone about the really interesting idea they contain; others are here only because they caught my eye when I first came across them.
]]>This is a Basic LTI 1.1 tool provider, to run Numbas exams in any LTI-compatible virtual learning environment.
The tool handles attempt data, and as well as offering CSV exports of student scores can report scores back to the host VLE's gradebook.
You must run your own instance of this tool - as well as using a fair amount of server resources, we don't want to keep other people's student data!
To install the tool, you need:
At most institutions, this will require the help of your IT team.
If you're unsure whether you can use the LTI tool provider, or want help setting it up, email the Numbas team.
The set-up process looks like this:
Warning: you could make a very strong argument I’ve thought far too much about something inconsequential. If that makes your stomach turn, look away now. This morning in the shower, I had an …
]]>Inspired by https://sites.google.com/site/geometryoftheprimes/ (ignore the nonsense)
]]>A site for me to collect recipes I like. I tried doing something clever with natural language processing to highlight the measurements in the ingredients, but it doesn't work very well.
]]>A talk about calculating π, given at Durham University for π day 2016. Contains lots of animations.
]]>This is the code which runs the website isthisprime.com.
Copyright 2016 Christian Lawson-Perfect
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
]]>All you have to do is say whether each number is prime.
]]>A clicker game where each click runs a program you've written. You get points for completing challenges, but each operation in your program costs points. It works OK, but I didn't have the motivation to finish it.
]]>A series of videos about mathematical objects.
]]>An interactive toy for the pancake-flipping problem, to go with a post by Katie Steckles on The Aperiodical. By repeatedly flipping the top part of a stack of pancakes, can you sort them by size?
]]>Generates nice designs made of subdivided circle quadrants.
]]>
Blackboard doesn't make it easy to analyse data to do with SCORM packages: the built-in SCORM reports don't give much useful information and are tedious to generate, and it's unclear where in the database the SCORM data lies.
We discovered that the Export/Archive Course tool collects together all of the attempt data for SCORM packages in a way that can be easily interpreted.
This tool allows you to analyse all of the information held by Blackboard about SCORM packages in your course, by presenting the data from a course archive nicely. It's designed to work with SCORM packages generated using Numbas; other packages might work, but we don't guarantee it.
We've found that Blackboard often loses data, or saves inconsistent data. That can mean that a student's attempt is missing, or the question scores don't tally up with the part scores, or the suspend data and correct answers don't match what the student saw.
This happens in a small but significant number of cases. It should be obvious on looking at an individual attempt whether there's something wrong, but bear in mind that inconsistencies can appear if you try to do analyse the set of attempts as a whole.
We've also had a report that if any student has unenrolled from a course, the export will stop as soon as it tries to match an attempt by that student with their Grade Centre entry. This means that tests will appear to have far fewer attempts in the analysis tool than are shown in the Grade Centre.
Finally, the reported durations of attempts only rarely match up with reality. Best not to try to draw any conclusions from those.
The tool is a self-contained Python Flask server, which you can run on your own PC. Install Python 3, and then install the required Python packages by running the following command:
pip install -r requirements.txt
Obtain a copy of the Blackboard SCORM analysis server (either clone this repository or download a .zip) and extract it into a directory on your PC.
To start the server, run
python server.py
And open http://localhost:5000 in your browser.
You can update a course's data by generating another archive and uploading that. The old version will be automatically rewritten.
]]>I found a load of corpuses of words. I made this to show the words that are in the intersections of several corpuses.
]]>A game about drawing lines
I never used real computer punchcards, so this is a simulation of how I think they worked. Click bits to turn them on or off, or in Lovelace mode you can't unpunch a hole! Shows the ASCII decoding of the card on the top.
]]>A thing to do a coordinate transformation on an image. The example it loads with transforms from polar to cartesian coordinates.
]]>It's a clever horse who can answer maths questions.
The code for the real-life Clever Hans is in the robot branch.
A little tool to help Cushing try out the birthday problem with cars - how many cars should you expect to drive past before having a 50% chance of seeing the same string of final 3 letters twice?
Type the registration of each car you see (or just the last three letters), and it'll tell you when you see a double.
]]>This goes with a Big MathsJam talk from 2015.
Dobble cards are points on a projective plane.
Each line contains all of the cards with a given symbol.
But a projective plane is its own dual: ifyou swap the lines and points, you get the same structure!
So there's no reason you shouldn't replace the symbols on a Dobble card with more Dobble cards.
]]>This extension provides functions to work with linear programs and other optimisation problems.
random_partition(n,k,[minimum=1])Generate a random partition of n into k parts, with smallest part at least minimum.
best_point(program)Solve a linear program with minimum constraints for each product, maximum constraints for each resource, numbers of each resource used in each product, and a straight line objective function
Looks at the following intersection points:
Returns the index of the intersection point giving maximum profit
best_coords(program)With program encoded as above, returns the coordinates [x,y] of the point giving the maximum profit
binding_lines(program)With program encoded as above, returns an array of booleans specifying which lines are binding (touching the optimal solution), from the following: [resource 1, resource 2, minimum x, minimum y]
nw_corner(supplies,demands)Use the NW corner algorithm to generate a first guess at an optimal solution to a transportation problem. supplies specifies the number of units supplied by each source, and demand specifies the number of units demanded by each destination.
Returns a matrix of the number of units to transport from each source to each destination.
nw_corner_display(supplies,demands)HTML representation of the stages of the NW corner algorithm
minimum_cost(supplies,demands,costs)Find the solution to the transportation problem which minimises total cost.
Returns a matrix of the number of units to transport from each source to each destination.
minimum_cost_display(supplies,demands,costs)HTML representation of the stages of the minimum cost algorithm.
shadow_costs(assignments,allocated,costs)Returns a list [m,rows,columns], where m is the shadow cost of each cell in the assignment matrix, and rows and columns give the shadow costs for each row and column, respectively.
assignment_is_optimal(assignments,costs)Returns true if the given assignment (matrix of number of units to deliver from each source to each destination) minimises the total cost.
assignment_is_valid(assignments,supplies,demands)Returns true if the given assignment is valid - the amount supplied from each source doesn't exceeed the available supply, and the amount delivered to each destination doesn't exceed the demand.
stepping_stone_works(assignments,costs)Returns true if the stepping stone algorithm to find an optimal assignment terminates.
stepping_stone(assignments,costs)Find an optimal solution to the given assignment problem, with the stepping stones method, starting with the given assignment.
Returns a matrix of assignments.
stepping_stone_display(assignments,costs)HTML representation of the steps of the stepping stone method.
assignment_cost(assignments,costs)Total cost of the given assignment
cost_table(supply,demand,costs)A table showing the cost matrix for the given assignment problem
assignment_table(assignments,supply,demand)A table showing the given assignment
show_cost_calculation(assignments,costs)LaTeX description of the calculation of the total cost of the given assignment
job_cost_table(costs,worker_name,job_name)A table showing the costs for each worker at each job. worker_name and job_name give the headings for workers and jobs, respectively (e.g., "Delivery driver" and "Route")
hungarian(costs)Perform the Hungarian algorithm to assign workers to jobs, minimising the total cost. Returns a matrix with 1 in the cell (worker,job) when the corresponding worker is assigned to the corresponding job, and 0 otherwise.
hungarian_display(costs)HTML representation of the steps of the Hungarian algorithm.
utility_set(utility,actions)HTML graph showing the given set of points in a 2D decision problem. utility is a list of 2d coordinates [x,y], and actions is a list of labels.
show_expected_value_criteria(utility,labels,prob_state_1,prob_state_2)HTML graph showing the utility set, with the expected value criterion line defined by prob_state_1 and prob_state_2.
evpi(utility,probabilities)Expected value of perfect information in the given decision problem, with the given (vector or list of) probabilities.
simplex(objective,equations)Solve the given linear programming problem with the simplex method
simplex_optimal_tableau(objective,equations)Matrix representing the optimal tableau when the simplex algorithm terminates.
simplex_find_bascs(tableau)List specifying which row each variable is basic in, in the given simplex tableau, or-1 if the variable is not basic.
simplex_display(objective,equations)HTML representation of the steps of the simplex method.
simplex_final_tableau(objective,equations)HTML representation of an optimal simplex tableau for the given problem.
convex_hull(points)The convex hull of the given list of points. Returns a list of points, in clockwise order.
]]>Released under the CC-BY 3.0 licence. Please give credit to Christian Lawson-Perfect.
]]>Inspired by the puzzle that my wife's grandma tests me with each time I visit.
Start with the number in the first box and follow the instructions until you get to the end. Enter your answer and press enter (or click on the timer).
Copyright 2015 Christian Lawson-Perfect
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
]]>A numbers-in-a-grid game.
Copyright 2015 Christian Lawson-Perfect
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
]]>A page that scrolls the digits of π endlessly, using Gosper's spigot algorithm.
]]>Christian Perfect has turned into a one-man plug for the holes in Wolfram|Alpha.
]]>The Word Lock puzzle from February 2015's puzzlebomb
]]>This extension provides a new data type and some functions to deal with polynomials
This extension adds a new JME data type Numbas.jme.types.polynomial, representing a polynomial in a given variable.
polynomial(expression in one variable)Create a polynomial, automatically detecting the variable name from the expression. This is quite strict about what it accepts - only one variable name, and coefficients and degrees have to be literal numbers, not calculations or references to other variables.
You can either write a literal expression, or pass a string. Note that if you use a literal expression, variables defined in the scope are substituted in. It's safer to use a string.
polynomial(x^2-2x+3)polynomial("5*x^4 + 2*x")polynomial(variable_name,coefficients)Create a polynomial in the given variable, with the given coefficients (coefficients[i] is the coefficient of variable_name^i). Example: polynomial(x,[-1,0,1]) represents the polynomial x^2-1.
mod_polynomial(expression,m) or mod_polynomial(variable_name,coefficients,m)As above, but all operations on this polynomial will be calculated modulo m.
p1+p2Add two polynomials
p1+n or n+p1Add a constant to a polynomial - more convenient than p+polynomial(n).
p1-p2Subtract p2 from p1
p1-n or n-p1Subtract a constant from a polynomial (or vice versa) - more convenient than p-polynomial(n).
p1*p2Multiply two polynomials
p1*n or n*p1Multiply a polynomial by a constant - more convenient than p*polynomial(n).
p^nTake polynomial p to the nth (integer, non-negative) power.
quotient(p1,p2)Divide p1 by p2, and throw away the remainder (polynomial quotient of p1 and p2)
remainder(p1,p2)Remainder when dividing p1 by p2.
mod(p,n)Take each coefficient of p mod n.
degree(p)Degree of p - highest power of the variable with a non-zero coefficient.
p1=p2Are p1 and p2 equal? True if all the coefficients match.
p[d]Coefficient of x^d in p.
eval(p,x)Evaluate the polynomial at the given point.
expr(p)A JME expression equivalent to the given polynomial; you can substitute this into the correct answer for a "Mathematical expression" part, for example.
string(p)A string representation of the polynomial.
latex(p)A LaTeX representation of the polynomial.
long_division(p1,p2)LaTeX rendering of the long division of p1 by p2.
Base object: Numbas.extensions.polynomials.Polynomial
(set it to a more convenient name, e.g. var poly = Numbas.extensions.polynomials.Polynomial)
new Polynomial(variable_name,coefficients,[modulo])coefficients is a dictionary of degree → coefficient. If modulo is given, all coefficients will be reduced modulo that number in any calculations using this polynomial.
Polynomial.from_tree(tree,[modulo])Create a polynomial object from a compiled JME tree
Polynomial.from_string(expr,[modulo])Create a polynomial object from a JME string
Polynomial object methodsp.evaluate(x)Evaluate at point x to a number
p.toLaTeX()Render as a LaTeX string
p.isZero()Is this polynomial zero?
p.degree()Degree of highest power term in p with a non-zero coefficient
p.negate()Negate every coefficient of p (returns a new polynomial)
p1.add(p2)Add p1 to p2
p1.sub(p2)Subtract p2 from p1
p1.mul(p2)Mutliply p1 by p2
p.pow(n)nth power of p
p.scale(n)Multiply p by constant n
p.add_degree(n)Add n to the degree of each term of p
p1.div(p2)Divide p1 by p2. Returns an object {quotient: <polynomial>, remainder: <polynomial>}
p.mod(n)Take each coefficient of p mod n (returns a new polynomial object)
p1.eq(p2)Are p1 and p2 equal?
p.coefficient(d)Coefficient of x^d in p.
Given a list of words, generates a wordsearch.
]]>Christian investigates the possibility of a moving whale being able to warn you it’s coming.
]]>This extension provides a new data type and some functions to deal with linear codes.
A collection of questions created using this extension is available to reuse.
This extension adds two new JME data types, Numbas.jme.types.codeword and Numbas.jme.types.code.
codeword(digits,field_size)Create a codeword from a list or vector of digits, in Z_{field_size}. For example, codeword([1,1,0,1],2)
codeword(word_string,field_size)Create a codeword from a string of digits, in Z_{field_size}. For example, codeword("11001",2)
zero(word_length,field_size)The zero word of the given length in the field Z_{field_size}.
is_zero(word)Is word a zero word (are all its digits 0)?
latex(codeword)A LaTeX rendering of the given codeword.
word1+word2Add two codewords.
word1-word2Subtract one codeword from another.
n*word or word*nMultiply a codeword by a scalar.
word[n]nth digit of codeword.
word[m..n]List of mth to nth digits of codeword.
weight(w)Hamming weight of the word.
allwords(word_length,field_size)Generate a list of all codewords of given length in Z_{field_size}.
random_word(word_length,field_size)Pick a random word of given length in Z_{field_size}.
random_combination(words)A random linear combination of the given words (from the field Z_p), i.e. a_0*w_0 + a_1*w_1 + ... + a_n*w_n where the a_i are elements of the field Z_p.
set_generated_by(basis)Returns the set of codewords generated by the given list of basis codewords.
is_generated_by(basis,words)Can all of the given words be written as a linear combination of the words in basis?
linearly_independent(basis)Are the given codewords linearly independent? (it checks that the set generated by the basis contains (field_size)^(number of basis words) words)
coset_containing(word,basis)Generate the coset containing the given word, with respect to the given generating set.
slepian_array(basis)Generate the Slepian array corresponding to the given basis set. Each row in the result is a coset, sorted by weight.
is_coset_leader(word,basis)Is the given word a coset leader in its coset? That is, does it have the minimum weight?
generator_matrix(words)A minimal set of generators for the linear code generated by the given words. If the words are linearly independent, you'll get the same number of words back, otherwise you'll get fewer.
parity_check_matrix(basis)Create a parity check matrix for the given generating set, by putting it in reduced row-echelon form I_n|A and returning -A_transpose|I_(m).
lexicographic_parity_check_matrix(words)Create a parity check matrix for the given generating set, with columns in lexicographic order.
hamming_parity_check_matrix(p,r)Create a parity check matrix for the Hamming code Ham_p(r). (p must be prime)
hamming_generating_matrix(p,r)Create a generating matrix for the Hamming code Ham_p(r). (p must be prime)
syndrome(word,pcm)Compute the syndrome of the given word with respect to the given parity check matrix.
reduced_row_echelon_form(basis)Put the given list of words (interpreted as a matrix) into reduced row-echelon form.
codeword_matrix(words)Returns a matrix whose rows are the given codewords.
hamming_square_encode(word)Encode word using Hamming's square code.
hamming_square_decode(word)Decode (and correct up to one error in) word using Hamming's square code.
hamming_encode(word)Encode word using the general Hamming code.
hamming_decode(word)Decode (and correct up to one error in) word using the general Hamming code.
hamming_distance(word1,word2)Hamming distance between two codewords.
hamming_ball(word,radius)All words within given Hamming distance of the given codeword.
hamming_sphere(word,radius)All words at given Hamming distance from the given codeword.
concat([words])Concatenate a list of codewords into one codeword.
len(word)Length of a codeword.
error(word,position)Introduce an error (change a digit) at the given position. Returns a new codeword.
check_array(word)Returns a LaTeX check-array for the given word, from a Hamming square code.
string(word)String representation of a codeword.
latex(word)LaTeX representation of a codeword.
code([words])Create a code from a complete set of codewords.
allwords(code)All words belonging to the given code.
minimum_distance(code)Minimum Hamming distance between words in code.
information_rate(code)A code's information rate
len(code)Number of words in code.
code[n]nth word in code. (in the order used when you created the code object)
code[m..n]List of the mth to nth words in code.
positional_permutation(code,positions)Permute the positions of the digits in code's words, following the given order.
symbolic_permutation(code,symbols)Permute the symbols in code's words, following the given order. symbols is a list giving a permutation for each digit. That is, symbols[i][j] says what symbol j should change to when in position i.
equivalent(code1,code2)Can code2 be obtained from code1 by a combination of positional and symbolic permutations?
find_equivalence(code1,code2)If code1 is equivalent to code2, find a combination of positional and symbolic permutations that maps code1 to code2.
Returns a dictionary with keys "positional_permutation" and "symbolic_permutation" when the codes are equivalent, and nothing if they're not.
find_positional_equivalence(code1,code2).If code1 is positionally equivalent to code2, find a positional permutation that maps code1 to code2.
Returns a list representing a permutation of codeword digits when the codes are positionally equivalent, and nothing if they're not.
find_symbolic_equivalence(code1,code2).If code1 is symbolically equivalent to code2, find a symbolic permutation that maps code1 to code2.
Returns a list representing a permutation of the symbols for each codeword digits when the codes are symbolically equivalent, and nothing if they're not.
distance_equivalent(code1,code2)Is there an isomorphism between the Hamming distance tables of code1 and code2?
Returns true if there is a permutation of the rows and columns of the Hamming distance table of code1 that produces the table for code2.
hamming_bound(field_size,word_length,errors_corrected)Hamming bound on the maximum number of codewords in a code with the given parameters.
singleton_bound(field_size,word_length,minimum_distance)Singleton bound on the maximum number of codewords in a code with the given parameters.
gilbert_varshamov_bound(field_size,word_length,minimum_distance)Gilbert-Varshamov bound on the minimum number of codewords in a code with the given parameters.
Everything lives under Numbas.extensions.codewords, which I'll omit from now on.
Codeword(digits,field_size)Create a codeword with the given digits, belonging to the field Z_{field_size}.
Codeword methodsword.toString()String representation of the word.
word.toLaTeX()LaTeX representation of the word.
word.toJME()JME representation of the word.
word.isZero()Is the word zero (are all its digits 0)?
word.eq(word2)Is this word the same as word2?
word.add(word2)Return a new word which is the sum of this word and word2.
word.sub(word2)Subtract word2 from this word (returns a new codeword object).
word.negate()Negate this word: w.add(w.negate()) = 0.
word.scale(n)Scale this word by n - multiply every digit by n.
word.weight()Hamming weight of this word - number of non-zero digits.
word.LaTeX_check_array()LaTeX rendering of a Hamming square code check array for this word (only makes sense if this word is a 9-digit binary word)
word.hamming_ball(radius)Find all words within radius Hamming distance of this word.
word.hamming_sphere(radius)Find all words with Hamming distance exactly radius from this word.
Codeword classfromString(str,field_size)Create a codeword object from a string representation, e.g. Codeword.fromString("01001",2).
sort(w1,w2)Comparison function to sort codewords lexicographically.
eq(w1,w2)Equivalent to w1.eq(w2).
zero_word(word_length,field_size)Create a zero word with the given length in the field Z_{field_size}.
allwords(word_length,field_size)Get all words of the given length in the field Z_{field_size}.
random_word(word_length,field_size)Get a random word of the given length in the field Z_{field_size}.
random_combination(basis)A random linear combination of the given words (from the field Z_p), i.e. a_0*w_0 + a_1*w_1 + ... + a_n*w_n where the a_i are elements of the field Z_p.
set_generated_by(words)Get all words generated by the given basis set.
is_generated_by(basis,words)Can all of the given words be written as a linear combination of the words in basis?
linearly_independent(words)Are all of the given words linearly independent of each other?
Words are linearly independent if l1*w1 + l2*w2 + .. +ln*wn = 0 has no solution other than l1=l2=...=ln=0.
coset_containing(word,basis)Generate the coset containing the given word, with respect to the given generating set.
slepian_array(basis)Generate the Slepian array corresponding to the given basis set. Each row in the result is a coset, sorted by weight.
is_coset_leader(word,basis)Is the given word a coset leader in its coset? That is, does it have the minimum weight?
hamming_distance(word1,word2)Hamming distance between two words.
reduced_row_echelon_form(basis)Put the given list of words (interpreted as a matrix) into reduced row-echelon form by reordering and taking linear combinations.
generator_matrix(words)A minimal set of generators for the linear code generated by the given words. If the words are linearly independent, you'll get the same number of words back, otherwise you'll get fewer.
parity_check_matrix(basis)A parity check matrix for the given generating set (which should be linearly independent)
lexicographic_parity_check_matrix(basis)A parity check matrix for the given generating set, with columns in lexicographic order.
hamming_square_encode(word)Encode word using Hamming's square code.
hamming_square_decode(word)Decode (and correct up to one error in) word using Hamming's square code.
hamming_encode(word)Encode word using the general Hamming code.
hamming_decode(word)Decode (and correct up to one error in) word using the general Hamming code.
syndrome(word,pcm)Compute the syndrome of the given word with respect to the given parity check matrix.
hamming_parity_check_matrix(p,r)Create a parity check matrix for the Hamming code Ham_p(r). (p must be prime)
hamming_generating_matrix(p,r)Create a generating matrix for the Hamming code Ham_p(r). (p must be prime)
Ham_p(r) is the p-ary Hamming code whose PCM has r rows
Code(words)A wrapper object for the code containing the given words.
Code methodscode.toString()String representation of the code - list all its words.
code.toLaTeX()LaTeX representation of the code.
code.toJME()JME representation of the code.
code.eq(code2)Is this code the same as code2? True if they have exactly the same words.
code.contains(word)Does this code contain word?
code.find_equivalence(code2)If this code is equivalent to code2, return an object {positional_permutation, symbolic_permutation}. If not, return null.
Two codes are equivalent if we can get from one to the other by a combination of positional and symbolic permutations.
code.equivalent(code2)Is this code equivalent to code2 - can you get from one to the other by performing positional and symbolic permutations on the digits of the codewords?
code.minimum_distance()The minimum Hamming distance between any pair of words in this code.
code.information_rate()Information rate of the code: log(number of words)/(log(2)*word length)
code.positional_permutation(order)Perform a positional permutation on the words in the code. order is a list, where column i is sent to order[i]. Returns a new Code object.
code.symbolic permutation(symbols)Perform a symbolic permutation on the words in the code. symbols is a list of permutations for each digit, where symbol j in position i is changed to symbols[i][j]. Returns a new Code object.
hamming_bound(field_size,word_length,errors_corrected)Hamming bound on the maximum number of codewords in a code with the given parameters.
singleton_bound(field_size,word_length,minimum_distance)Singleton bound on the maximum number of codewords in a code with the given parameters.
gilbert_varshamov_bound(field_size,word_length,minimum_distance)Gilbert-Varshamov bound on the minimum number of codewords in a code with the given parameters.
mark_codeword_set(part,field_size,fn)To be used on a "match text pattern" part, where the student's answer is a list of codewords separated by commas.
Checks that the student's answer is valid, and parses it to a list of Codeword objects in the given field, then calls fn(words). In fn, this refers to the given part object.
validate_codeword_set(part)Validate a part marked by the above function. Shows a warning message if the student's answer can't be interpreted as a list of codewords separated by commas.
]]>This report provides buttons to download a spreadsheet of scores for a SCORM package. It differs from the basic report by giving the total score in raw marks and percentage, as well as scores for each SCORM objective (called "question").
It's designed for Numbas tests, so it assumes there's only one SCO and that objectives map to questions.
Clone this repository into mod/scorm/report/download_scores, then go to Site administration → Notifications and click to install it.
Moodle availability condition plugin. Allows you to restrict access to course sections or activities by username.
Clone into availability/condition/username. Go to "Site administration -> Notifications" and click to install the plugin.
This extension adds a new data type to the Numbas JME system, representing elements of permutation groups (really, just S_infinity, but you can pretend you're working in an S_n).
Permutations are stored as bijections on the natural numbers.
The symbol for the identity permutation is e by default. You can change this by setting Numbas.extensions.permutations.identity_symbol in the question preamble.
This extension adds a new JME data type, Numbas.jme.types.permutation, representing a permutation.
permutation(map) or perm(map)Create a permutation. map is a list of numbers. map[i] is the image of i under the permutation.
Note: Because lists are zero-indexed, the first element is 0, not 1.
Example: permutation([1,0]) is the permutation swapping the first two elements and will be displayed in cycle notation as (1,2).
permutation(str) or perm(str)Create a permutation. str is a permutation in disjoint cycle notation.
Example: permutation("(1,2)(3,4,5)")
transposition(a,b)A permutation representing the transposition of the elements a and b.
rotation(n,r)Create a permutation representing a rotation of the numbers 1 to n by r places.
If r is not given it defaults to 1.
flip(n)Create a permutation representing a reflection of the numbers 1 to n.
compose(p1,p2) or p1*p2Compose permutations. The action is on the left - (p1*p2)[x] = p1[p2[x]].
p^nThe nth power of p. p^0 is the identity permutation.
p[x]Apply permutation p to the number x.
Consider p as an element of S_n; any x greater than or equal to n is mapped to itself.
inverse(p)Returns the inverse of permutation p.
even(p)Is p an even permutation (can it be written as a product of an even number of transpositions?)
size(p)The largest number n that is permuted by p, or equivalently, the smallest n such that p is a member of S_n.
order(p)The order of p: the smallest power of p equivalent to the identity permutation.
cycles(p)Calculate all of p's cycles. Returns a list of lists of numbers.
nontrivial_cycles(p)Calculate all of p's cycles of length greater than 1.
show(p)Render p as a TeX string (just substituting p into a TeX expression will also work, but you can use this if you want to manipulate the TeX string somehow)
twoline(p)Render p in two-line form (numbers 1..n on top row, their images on the bottom) as a TeX string
as_transpositions(p)Render p as a product of transpositions, in TeX.
p1=p2Are p1 and p2 the same permutation? True if p1*inverse(p2) maps n to n for all n.
is_disjoint(str)Returns true if str is a representation of a permutation as disjoint cycles (no two cycles have an element in common).
is_transpositions(str)Returns true if str is a representation of a permutation as transpositions (each cycle has length 2).
is_rotation(p)Returns true if p is a rotation, i.e. a permutation of the form f: i -> (i+x) mod N, for some 0< x < N.
is_flip(p)Returns true if p is a flipped rotation, i.e. a permutation of the form f: i -> (x-i) mod N, for some 0
Looks like some wiggly worms swirling round a vector field. Made with p5.js.
]]>A JavaScript implementation of David Carlisle's Content-to-PresentationMathML xsl
]]>A couple of macros that let you use MathJax to render LaTeX in a Twine story.
In your StoryIncludes passage, add the line
mathjax.twee
and either make sure that that file is in the same directory as your story file, or use its full path.
For inline maths, use <<math>>.
For display maths, use <<dmath>>.
<<math e^{i \pi} + 1 = 0 >>
There's an example story in mathjax-demo.tws. You can play it at christianp.github.io/mathjax-twine.
Public domain or Apache 2.0. I don't care how you use this!
]]>Split the space into quadrants. Name them 1,2,3 and 4. Split those quadrants up, and add another digit to the names. Repeat a few times!
Now each cell has a several-digit name. Turn a cell on or off depending on whether its name passes a regular expression. Use the length of the expression's matching groups to colour on cells.
Inspired by SSOΔ.
]]>This morning Katie and I had a little discussion about house style on The Aperiodical. Mathematican Paul Taylor was listed as “Mathematician Paul Taylor” in the blurb for his featured p…
]]>Made for James Grime - see the video at YouTube.
The aim is to place flags on a field of length 1, so that when you divide the field into equal-sized plots, there's exactly one flag in each field. To complicate matters, you place the flags one-by-one, and you check whether the placements are valid after each flag goes down.
made by cp. Released under the MIT licence.
This uses d3, jQuery, normalize.css, and a picture of a house from Emoji One, which all have their own licences.
]]>This is a Flask app which serves a new frontend to the OEIS.
The aim is to produce pages with non-ancient HTML, and then to pay attention to visual presentation.
The OEIS server happily returns text files for its entries, but I don't know how happy they'd be about getting lots of traffic that way, so this is just a hobby project for me for now.
]]>An obnoxiously large implementation of the programming language I can't name.
]]>A little tool to visualise solutions to first order ODEs.
It uses
See it running at http://www.staff.ncl.ac.uk/christian.perfect/ode-solution-plot/
]]>A library for parsing, evaluating and rearranging algebraic expressions in JavaScript.
This project is no longer maintained. Development continues in https://github.com/numbas/Numbas.
The name "LissaJS" was picked without much thought. It's meant to pun on "Lissajous", and follows the convention that every Javascript library's name ends in "JS", but apart from that it hasn't got much going for it. Suggestions of better names are welcome at issue #1.
This is a spin-off of the Numbas e-assessment system. It has no external dependencies.
It's currently provided on a "do what you can with it" basis; we'll add examples and documentation later.
There's an interactive demo page at http://numbas.github.io/LissaJS/.
Include the lissajs.js script in your page.
<script src="proxy.php?url=https%3A%2F%2Fsomethingorotherwhatever.com%2Flissajs.js"></script>
It uses some ECMAScript 5 features. For older browsers, you'll also need to load es5-shim.
There is an explanation of the syntax, data types supported, and a function reference at http://numbas-editor.readthedocs.org/en/latest/jme-reference.html. Just replace Numbas with LissaJS. For more in-depth code documentation, see http://numbas.github.io/Numbas/ - again replacing Numbas with LissaJS.
All operations happen with respect to a LissaJS.jme.Scope object. There's a default scope at LissaJS.jme.builtinScope containing all the built-in functions and rulesets. A few of the most commonly-used functions are available through the LissaJS object, using this default scope implicitly, for your convenience.
To evaluate an expression:
LissaJS.evaluate('expression',{dict of variables});
or, with a custom scope:
scope.evaluate('expression',{dict of variables});
To compile an expression to a syntax tree:
scope.compile('expression');
To convert an expression to LaTeX:
LissaJS.exprToLaTeX('expression',[rules]);
or, with a custom scope:
LissaJS.jme.display.exprToLaTeX('expression',[rules],scope);
To simplify (rearrange) an expression:
LissaJS.simplifyExpression('expression',[rules]);
or, with a custom scope:
LissaJS.jme.display.simplifyExpression('expression',[rules],[scope]);
There are lots of pure maths functions under LissaJS.math.
LissaJS uses code from the Numbas project.
Copyright 2011-16 Newcastle University
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
You can see a plain-English explanation of the license and what it allows at tl;drLegal
]]>A page to try out different MathJax configurations.
]]>Binding handlers for knockout.js which typeset all LaTeX inside an element containing HTML, or just typeset some LaTeX on its own.
Demo at http://checkmyworking.com/misc/knockout-mathjax-bindings/
]]>The Herschel graph has some pretty cool properties. Christian Perfect constructed the associated polyhedron, and it too has some cool properties!
]]>My friend David Cushing and I used to review integer sequences for fun.
]]>Christian was asked to find some art to decorate the walls of the university maths department he works in. Here’s what he found.
]]>A template to create slides with deck.js, MathJax, and the unicode Computer Modern fonts.
This contains everything you need to create a slideshow presentation which runs in the browser but uses the Beamer fonts and supports LaTeX maths.
deck.js is the best tool I've found to make HTML presentations.
I recompiled the cm-unicode project's Unicode version of the Computer Modern fonts (the default fonts used by LaTeX) so they could be used on the web. They can be downloaded from http://checkmyworking.com/cm-web-fonts/.
MathJax does the maths rendering.
You can see an example presentation at http://checkmyworking.com/misc/pgps-computable-groups-feb-2013/.
]]>A talk about the stuff I was looking at in my aborted PhD.
]]>Conway's Game of Life, but it doesn't all happen at once
]]>A talk about zero-knowledge protocols, given at Newcastle's postgraduate forum.
]]>I compiled the Computer Modern fonts into a format that can be used on the web.
]]>A simple page for rendering LaTeX at any size, using MathJax.
Copyright (c) 2012 Christian Perfect
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]>jQuery.writemaths
Adds write maths, see maths to comment boxes, and to TinyMCE editor instances (in particular, the post editor).
Requires MathJax to be available to wp_enqueue_script under the name 'mathjax'. The Simple MathJax plugin does this.
Copyright 2014 Christian Perfect
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
(tl;dr)
]]>The blog I run with Peter Rowlett and Katie Steckles.
]]>A talk about how to use MathJax, from when it was new.
]]>About the 'Princess in a castle' puzzle.
]]>Princess in a Castle puzzle!
see http://checkmyworking.com/misc/princess-castle
uses Paper.js.
]]>An investigation of what happens if you make the Sieve of Eratosthenes randomly.
]]>This is a sporadic series of posts where I collect links to interesting or unusual maths things.
]]>A talk about some physical mathematical objects, given at Newcastle's postgraduate forum.
]]>A cross between tic-tac-toe and Reversi, apparently.
]]>Asteroids, played on the projective plane or the Klein bottle.
]]>I wanted to make interactive demonstrations of some codes and ciphers. I only got as far as doing the Caesar Cipher.
]]>This is a simple script which asks for your date of birth and then calculates when your next prime birthday is.
A prime birthday is when you are a prime number of days old!
]]>This repository contains things I have written using Monkey.
Some of the things may not work, some may work but have no purpose, and others may work and be useful but no fun at all.
]]>To use it, just call $(selector).writemaths()$ when the page loads.
You can optionally pass in an object with some options:
cleanMaths: a function which does something to the LaTeX before displaying it.callback: a function which is called when the preview is displayed.iFrame: set this to true if you're applying writemaths to something like TinyMCE, which lives inside an iframe.position: the position on the parent element on which to place the preview. It's really the `at option of jquery.ui.position.previewPosition: the position on the preview element to line up with the parent element. It's really the my option of jquery.ui.position.of: the element to position relative to.I usually write up my thoughts informally on my blog before I TeX them up.
]]>Numbas is an open-source system for creating tests which run entirely in the browser. It has been developed by Newcastle University's School of Mathematics, Statistics and Physics.
For more information about Numbas and what it does, see our website at numbas.org.uk.
Documentation for Numbas users is at numbas-editor.readthedocs.org.
This repository contains the Numbas compiler, which runs as standalone Python 3, but the most convenient way to use Numbas is through the web-based editor.
A publicly-available editor, requiring no set-up, is available at numbas.mathcentre.ac.uk. Or, you can follow our instructions for Windows, Mac, or Ubuntu to install your own instance.
If you decide to run your own installation, install the compiler's dependencies with pip install -r requirements.txt.
This repository is just one part of the Numbas ecosystem. See the numbas organisation for the other pieces, including the web-based editor, extensions, and VLE integrations.
Numbas is open source, and we welcome contributions of any sort. Bug reports or feature suggestions can be added to the GitHub issue tracker, or emailed to [email protected].
See our page on contributing to Numbas for more information on how you can help.
We keep a list of tasks specifically for new contributors, under the good-first-issue label. There's a corresponding list in the editor repository, too. These tasks should be fairly straightforward to implement without much knowledge of how all the code fits together.
This tool runs on the command line: run python bin/numbas.py to see the options. You can give it the name of a .exam file or pipe one in.
When making changes to the JavaScript runtime, it's a good idea to run the unit tests in the tests directory. Start a local web server with python -m http.server and go to http://localhost:8000/tests. The file jme.html contains tests to do with the JME system, and parts.html contains tests to do with the part marking algorithms.
If you make a change, please try to add unit tests to confirm that Numbas behaves as expected.
The Makefile in this repository collects together scripts to run the unit tests, and builds the API documentation. Linux and Mac OS have built-in support Makefiles, but Windows doesn't. On Windows, cygwin provides make.
API documentation for developers is at numbas.github.io/Numbas.
This is generated using JSDoc, with a custom template.
Run make docs to rebuild the API documentation into ../numbas-docs.
Copyright 2011-18 Newcastle University
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
You can see a plain-English explanation of the license and what it allows at tl;drLegal
Copyright in the content produced using Numbas resides with the author.
]]>Subdividing a triangle into circles.
]]>A talk about Turing completeness, given at Newcastle's postgraduate forum.
]]>checkmyworking.com began as place to put notes about my PhD work, before I abandoned it. I also used to post about maths things, before I started The Aperiodical. Now (in 2022), my plan is to use this site to post bits and bobs about the techy things I'm doing, for the sake of anyone else who might encounter the same problems I do.
]]>02/02/09: Actually run for the actual hills, it's the first commit!
]]>Some greetings cards I made.
]]>A nonsense story
]]>Some rubbish about exams?
]]>A series of cartoons
]]>