John Graham-Cumming's blog
- Posts
- 540 from 25 Nov 2005 to 9 Sep 2026
- Posting
- monthly (9 in the last year)
- Kind
- personal blog
- Feed
- https://blog.jgc.org/feeds/posts/default
2026 8
- Don't stick your SidePulse Pro in a Mac Studio
- For no apparent reason Oral-B removed flossing from their app
- The eerie interface of man and machine (Life Magazine, October 1967)
- The aperiodic table
- Raspberry Pi as an isolated AI coding server
- Maistro
- The European Schuko socket bothers me
- There's a ridiculous amount of tech in a disposable vape
2025 17
- If you care about security you might want to move the iPhone Camera app
- A couple of handy KNX gadgets
- Protecting your (my) passport from liquid damage
- Is a movie prop the ultimate laptop bag?
- Ex x
- Ode to the anti-Mac: Panasonic Toughbook FZ-40
- Pimping my Casio: Part Deux
- The discreet charm of the infrastructureless
- Low-background Steel: content without AI contamination
- It was time for a dim bulb current limiter
- Just one more calculator, bro, just one more, I swear it's the last one
- The Via Verde Stick
- Debugging Lotus 1-2-3 by fax
- I have come to loathe acronyms
- Archiving hardware projects
- Getting the KIM-1 to talk to my Mac
- Twenty years of the "GNU Make Standard Library"
2024 19
- The complete text of "All Watched Over by Machines of Loving Grace"
- badkeming: a site for kerning so bad it's keming
- "All your base are belong to us" introduction to my 2004 MIT Spam Conference talk
- Personalized voice recordings by Elwood "You've got mail!" Edwards
- Rabbit hole: stumbling across two Portuguese punched cards
- I made a rubbish clock
- Cracking an old ZIP file to help open source the ANC's "Operation Vula" secret crypto code
- Steve Ballmer's incorrect binary search interview question
- Two ways to use an LED as a light sensor with Arduino
- Acorn Computer Systems catalogue circa 1983
- Fixing my iPhone 13 Pro cameras at -18C
- Controlling the Taylor Swift Eras Tour wristbands with Flipper Zero
- The Economist's ugly new print typeface
- The formal development of secure systems (my 1992 doctoral thesis)
- Taschen, Acorn and Knoll's Law
- The Acme Klein Bottle (from Cliff Stoll and family)
- The original WWW proposal is a Word for Macintosh 4.0 file from 1990, can we open it?
- Repairing (sort of) a Dyson fan remote control
- My daily driver is older than I thought; it's positively vintage!
2023 30
- Compression of the lyrics of the "12 Days Of Christmas"
- Complete restoration of an IBM "Butterfly" ThinkPad 701c
- Using my Minitel 1B over the phone network in 2023
- My primality testing code is 6x faster than Sir Roger Penrose's
- Reading the Lovelace Foundation's Nuclear Bomb Effects Computer
- My 1976 KIM-1
- Why is the Internet so popular? The answer according to a 1995 ThinkPad 701c
- Cyberdecking the Raspberry Pi 400 on the Eurostar to Brussels
- "Hacker News" for retro computing and gaming
- The demise of my Aiwa HV-MX100 VCR
- Weekend bricolage: a lightbox for photographing electronics for under €15
- Weekend bricolage: an aroma picker for the Le Nez du Vin game
- Breathing life back into a Minitel 1B with the Minimit!
- SwissMicros' beautiful "HP" calculators
- Retrieving 1TB of data from a faulty Seagate Firecuda 530 drive with the help woodworking tools
- The Easter Egg that wasn't in Mission: Impossible – Dead Reckoning Part One
- Revisiting the "Cansole", my silly Pong game, because my TV doesn't have the right connectors
- Is it hard to design a mute button or something?
- Unfortunately, Kelly Rowland couldn't have used the =HYPERLINK() function to message Nelly
- How to beat an adaptive/Bayesian spam filter (2004)
- A little bit of SMD work on a bank holiday Tuesday
- A better case for my Inkplate 10-based weather device
- An unexpected journey: making the Yogasleep Travel Mini play the theme from Kung Fury
- Bringing the POPFile web site back from the dead
- A personal weather "picture" using the Soldered Inkplate 10 and Pirate Weather API
- The inner beauty of a Tesla Stropkov DS 3600 telephone from the early 1980s
- I don't have Twitter any more (updated Feburary 2024)
- Barebones project showing how to get an Inkplate 10 to talk to WiFi and the Internet over HTTPS using the Arduino IDE
- WAVE OK! The WWVB time signal sets my clock in Portugal for the second time
- Repairing a tiny ribbon cable inside a 28 year old IBM ThinkPad 701CS
2022 24
- Sea Glass: a "breathing" night light
- The Rogers Watch: a retro display and all the insides on the outside
- Restoring my Commodore P50 calculator with a little solder and a 3D printer
- Restoring my Rockwell 8R calculator with a 3D printer
- Primality testing on the Commodore P50 programmable calculator with one register and 23 steps
- The hidden "code" inside 2-XL's space age noises
- Childhood toys: 40 years on 2-XL works just fine
- The Halloduino: a microcontroller-powered Hallowe'en lantern
- Adding a mode switch to my Minitel 2
- Pimping my Casio with Oddly Specific Objects' alternate motherboard and firmware
- Updated my weather forecasting Totoro to use the Portuguese weather service
- Behind The Screens has a new home away from YouTube
- GNU Make Standard Library project moved to GitHub
- Adding a clock signal to my self-contained AVR programmer
- A self-contained AVR programmer using an Arduino Uno
- Guest WiFi using a QR code
- The parable of the form request form
- The mysterious behaviour of the Flying Tiger Countdown Clock (and the bug that causes it)
- Ripping old mini DV video tapes on a Mac
- Setting up and running the MAME emulation of the Minitel 2
- Voiding the warranty on a 1993 Minitel 2 to run arbitrary firmware
- Using a Minitel 1B as a serial terminal
- Resurrecting a Dataman S4 PROM programmer
- The "Roman Holiday" teleprinter code
2021 6
- Making an old USB printer support Apple AirPrint using a Raspberry Pi
- Receiving the WWVB time signal in Portugal (by accident)
- Cloudflare's SSL/TLS recommender on plan28.org
- Moving jgc.org to Cloudflare Pages
- Aeronear: an ambient device showing nearby aircraft
- Dividing n elements into groups of at least size g minimizing the size of each group
2020 2
2018 2
2017 4
2016 11
- Sessions I moderated at the Cloudflare Internet Summit
- The CloudFlare Internet Summit
- Lazy GNU make variables
- Le Sixième Élément : le code source dans le cinéma
- Solving the SETI Decrypting Challenge
- Setting up CloudFlare's Universal SSL and Origin CA on Plan 28
- What the "Silicon Valley" Easter Egg code does and how
- Two factor paper passwords
- Apple's amusingly round reuse figures
- Making the BBC micro:bit display the BBC Micro owl logo
- Finding free images for commercial use
2015 11
- The secret message hidden in every HTTP/2 connection
- Possibly the machines that injected JavaScript in pre-revolution Tunisia
- Dirty Go: eliminating vertical space
- The effectiveness of Turing's Vigenère cipher breaking technique
- Another way to find the value of GNU make's -j parameter
- One weird trick that will give you makefile X-ray vision
- Plain web text offenders: sending my location over HTTP when HTTPS was possible
- The one line you should add to every makefile
- "The GNU Make Book": probably more than you ever wanted to know about make
- GNU make insanity: finding the value of the -j parameter
- Failing to RTFM for Go: defer statements
2014 4
2013 39
- Mothballed
- Debugging for High-altitude Balloon Enthusiasts (and others!)
- Slides from Go London User Group September 2013 meeting: Go Memory
- Why is the fifth part of the GCHQ "Can you find it?" challenge so easy
- SIGINT Reading List
- OSCON 2013 Keynote: Turing's Curse
- Your test suite is trying to tell you something
- Write good commit messages
- The Plain Mail Snail: One way to make people switch to using encrypted email
- The hollow triangular numbers are divisible by three
- Some things I've learnt about public speaking
- Winner of the minimum coin problem
- The Four Power Days
- A home made periodic table
- The two problems I had to solve in my Oxford interview
- From the ground up (or how to encourage a school boy)
- How I coded in 1985
- The minimum coin problem (with prize)
- Update on Go 1.1 crypto performance
- The importance of open code
- PDF and eBook version of "A non-mathematical introduction to one-way functions and their use in securing passwords"
- Bob's wife Carla lends a speedy mind
- Alice strikes back against Bob's 'reverse dictionary'
- Possible solutions to Leo Marks' security conundrum
- Bob outsmarts Alice's 'one way function'
- A non-mathematical explanation of one way functions
- lulip: a line-level profiler for LuaJIT
- Performance of array creation in Lua
- Automatic bookmarking of locations in Emacs
- A simple illustration of Go interfaces
- Some Pig
- Updated list of my GNU Make articles
- How to fix a corrupted JavaScriptCore.framework on Mac OS X 10.7.5
- The greatest Google Mail feature you may not be using stopped working
- Integrating OpenSSL crypto functions with Go
- Lua implementation of Aho-Corasick string matching
- GMSL v1.1.3 Released
- Calling OpenSSL libcrypto functions from Go
- Archived posts from "Double Stealth"
2012 77
- Speeding up HTTP with minimal protocol changes
- Listen on a UDP port and dump received lines of data
- How I ended up with so much Hacker News karma
- A home-made Voltaic Pile
- The greatest Google Mail feature you may not be using
- The Bergamo Analemma
- "That'll Never Work!"
- The Rizzoli Conundrum
- Tempio Voltiano
- "Calculation and Tabulation in the Nineteenth Century: Airy versus Babbage"
- A downloadable nanosecond
- Plan 28 now accepting donations
- Web-scale Arduino
- The Great Railway Caper: Big Data in 1955
- Fact checking George Dyson (where he taps me on the shoulder)
- My UKHAS 2012 Conference talk: "HAB Software Woes"
- A water rocket made from household bits and bobs
- The UK has an entire IPv4 /8 that it isn't using (UPDATED)
- The Joy of Bit-Banging
- Conversion of cheap optical mouse to robot odometer
- An intuition/reality clash in Go
- A Go Gotcha that Got Me
- Automatic detection of a Google Mail log in
- CloudFlare's ScrapeShield spots single line of text copy/pasted from my blog
- Why don't they just..?
- Security questions are salt
- Curiosity rover writes Morse Code of JPL everywhere it goes
- My TED Talk: The Greatest Machine That Never Was
- The TENMA 72-8730 IR Thermometer
- Some things I've learnt about writing
- Tim Robinson joins Plan 28
- Some things I've learnt about programming
- The Perl script the powered the Alan Turing petition
- Things I like about programming in Go
- Waiting around for something to happen in Go
- What the final Turing Machine Google Doodle does
- Is there a 'Moore's Law' for web pages?
- New Scientist's Instant Expert on Alan Turing now free to non-subscribers
- Autograms
- One way to fix your rubbish password database
- Animated solution to the "Never gonna give you up" program problem
- Compression of the lyrics of "Never gonna give you up" using the Bentley/McIlroy algorithm
- To boldly Go where Node man has gone before
- Simonoids. It's 'Simon' in an Altoids can powered by Arduino
- Patching the Internet
- Reverse authentication for banking
- Make your own 'prime factorization' diagram
- tacoli: a simple logging format
- The Greatest Machine That Never Was
- Deglitching a Sparkfun 7-segment Serial Display
- Getting around the London 2012 branding police
- More support for open software in science
- Brief Plan 28 Update
- Bletchley Park is Blooming
- In praise of... text files and protocols
- A quick phone navigation tip
- Met Office and CRU argue for open data and open code
- Ambient bus arrival monitor from hacked Linksys WRT54GL
- Teaching yourself Morse Code with iPhone's Accessibility options
- Nine times table on your fingers (and algebraic explanation)
- How to divide by 9 really, really fast (and why it works)
- The Delilah Secure Speech System
- How to break the 'rapper code'
- Programmer
- Mobile subscriber leakage in HTTP headers in the wild
- The Case for Open Computer Programs
- Alan Turing's reading list (with readable links)
- So much for Google's Privacy Settings
- My foxhole radio
- UK Government declines to pardon Alan Turing
- Long range WiFi antenna from Illy coffee can
- Using GNU Make's 'define' and '$(eval)' to automate rule creation
- 2012 Plan 28 Media Coverage
- GAGA-2: Mounting the flight computer
- GAGA-2: Distance RTTY Test
- Gaga 2
- International Object Sizing Tool
2011 79
- A simple illustration of the use of goroutines and channels in Google Go
- A pre-Christmas Plan 28 update
- A back channel confirms that I'm right... sort of
- Down the GCHQ rabbit hole (or I think there really is a part 4)
- Is there another GCHQ Code Challenge?
- Downton Abbey Series 1 Episode 1 Morse Code
- A nice bottle of Lorem Ipsum 2009
- The Lack of Affordance Problem
- How to forcibly eject a CD/DVD from a MacBook Air USB SuperDrive
- Back from the dead with a power supply repair: my BBC Micro Model B
- Turning GE Color Effects G-35 Christmas Lights into a 7x7 display with Arduino
- My BBC Micro Model B and a plume of acrid smoke
- The Demon Machine
- Action this Day. Here's how to help Bletchley Park
- 2011 Plan 28 Media Coverage
- A program waits
- Lovelace's Leap
- Plan 28: Analytical Engine project gets underway
- The Pie Chart Challenge
- A security conundrum in Between Silk and Cyanide
- 'Sea trials' of a commercial 'Ponyo' boat
- Text and video of my OSCON 2011 Keynote presentation
- List of English words that can be spelt using elements from the periodic table
- GNU Make Recursive Wildcard Function
- Pronounceable IPv6 addresses, WPA2-PSK and hashes
- More experiments with expanding polyurethane foam
- The complete text of Gordon Brown's apology to Alan Turing
- Plan 28 is gathering steam
- Further experiment with expanding polyurethane foam
- Density of expanded polystyrene and expanded polyurethane foam
- Experiments with polyurethane foam for injection molding a HAB capsule
- Making a 'Ponyo' or putt-putt boat
- Mean and standard deviation of the distances between letters
- Importing an existing SSL key/certificate pair into a Java keystore
- Identification of homophone sequences in the Zodiac 408 cipher
- How The Zodiac enciphered the Zodiac 408 cipher
- My Email Canary
- Plan 28: June 2011 Update
- How to break the New Scientist cipher puzzle (part 3)
- Conversion of miles to kilometers (and back) using addition only
- Playing Pong on a pair of candy cans: it's the Cansole!
- The code on XIII : The Series
- O Atlas Geek: 128 Lugares Onde a Ciência e a Tecnologia Ganham Vida (The Geek Atlas in Portuguese)
- Next can-traption: Video Games Can-sole
- OSCON '11 Keynote: Turing, Perl and Palpatine.
- How to add iPod integration to a TV the cheesy way
- GAGA-1 Release Photos
- GAGA-1: The Flight
- All telemetry received from GAGA-1
- Internal log of GAGA-1 recovery computer
- Internal log of GAGA-1 camera
- GAGA-1: Quick post-flight update
- GAGA-1 looking good for Sunday
- GAGA-1: Integration testing
- Tentative launch date for GAGA-1: April 10, 2011
- GAGA-1: Parachute Test
- The Piral
- Geek Weekends
- GAGA-1: The Stack
- Geek Weekend: Charles Darwin's Home
- GAGA-1: flight computer testing
- GAGA-1: Another little freeze test
- GAGA-1: Working flight computer
- GAGA-1: The lovely sound of RTTY
- How could I have coded this better?
- GAGA-1: Parachute
- GAGA-1: Temperature measurement
- GAGA-1: Batteries
- GAGA-1: Voiding the warranty (for great justice!)
- Hacking the Toshiba T4800CT (a love story)
- Over 1,000 messages of support for Plan 28
- The State of Plan 28
- Calculating, rather than experimenting to find, resistor values for the NTX2 voltage divider
- The Genius of Turing
- Voltage divider calculator for Radiometrix NTX2
- GAGA-1: Flight Computer radio's first transmission
- GAGA-1: Getting close to completion
- The code injected to steal passwords in Tunisia
- A couple of videos of the Bombe cracking Engima at Bletchley Park
2010 95
- Why do Christmas lights all go out when one bulb blows? (and how to find the broken one)
- My password generator code
- Royal Festival Hall condundrum
- Write your passwords down
- Plan 28 gets some professional PR
- Don't write to me asking me to support your crusade against global warming science
- Shift Run Stop
- Backgrounder document on Plan 28
- If you want to understand the Analytical Engine, start with the Difference Engine No. 2
- Babbage books as stocking stuffers
- A boost for Plan 28
- GAGA-1: The Camera Hole
- Breaking the Reddit code
- GAGA-1: CoCom limit for GPS
- GAGA-1: Capsule insulation and antenna mounting
- Notes on Kryptos Part 4
- A proper Dr.
- GAGA-1 Recovery Computer Ground Test
- I guess Hacker News doesn't do meta very well
- The things make got right (and how to make it better)
- GAGA-1 Recovery Computer
- The most common objection to my 'releasing scientific code' post
- All Plan 28 Media Coverage
- A Plan 28 completion date
- Plan 28: the first 10 days
- What Nature didn't say
- Plan 28 (Analytical Engine) FAQ
- 1,000 people sign the Plan 28 pledge
- A big boost for Plan 28
- Geek Weekend: Kew Bridge Steam Museum
- 1,000 (bad) ideas
- The Analytical Engine vs. The ZX81
- Let's raise £500k for the Analytical Engine
- Charles Babbage and Climate Change
- Three Silicon Valley places Paul Graham omitted
- The 100-year leap
- GAGA-1: Computer mounting
- Start The Engine
- More technical opinions on the Analytical Engine project
- What to use instead of a pie chart
- A quick chat with Doron Swade
- Plan 28
- Another technical opinion on building the Analytical Engine
- Autotools
- Babbage machines in Meccano
- First opinion on whether the Analytical Engine could be built
- It's time to build the Analytical Engine
- GAGA-1 Source Code Repository
- GAGA-1: Capsule paint job
- Babbage's Debugger
- GAGA-1: Weight Budget
- GAGA-1: The Capsule
- GAGA-1: 2,766 tedious photographs and a log file
- GAGA-1: "Honey, there's a camera in the freezer"
- GAGA-1: Redundancy and Independence
- GAGA-1: Camera Hacking
- The Geek Atlas now available for Kindle
- An interview with me about The Geek Atlas
- On the release of scientific source code
- Eating your own dog food in the 1960s
- The bandwidth of a fully laden 747
- Monte Carlo simulation of One Banana, Two Banana to develop a card counting strategy
- GAGA-1: Some initial investigations
- The Geek Atlas Companion
- BallastHalo 5 launch afternoon out
- Gaga 1
- The CCE Review report
- Bayes, Bletchley, JN-25 and a 'modern' optimization
- An interview with me about The Geek Atlas
- Lots of domains are using crackable DKIM RSA keys
- Tribute to Alan Turing by a Second World War WREN
- Duodecimal
- The Elevator Button Problem
- A final reply about awarding a Knighthood to Alan Turing
- Facebook's DKIM RSA key should be crackable
- Your last name contains invalid characters
- 10:10 Code FAQ
- The 10:10 Code
- How to write a successful blog post
- How to sleep on a long haul flight
- Talking to Porgy
- The Offal Tower
- Squaring two digit numbers in your head
- Goodbye HP Procurve Access Point 420
- London Transport Museum: Acton Depot Weekend
- A welcome bunch of amateurs
- A bad workman blames his tools
- If you're searching remember your TF-IDF
- £1,000 for Bletchley Park thanks to The Geek Atlas
- Price drop on GNU Make Unleashed
- Update list of my GNU Make articles
- Mendelian Randomization: getting genes to run randomized trials for you
- Simplifying my OLPC XO-1 Temperature Sensor
- The Ikea Lillabo Processing code
- More fun with toys: the Ikea LILLABO Train Set
2009 31
- Toy decoding: vtech Push and Ride Alphabet Train
- Parsing a JSON document and applying it to an HTML template in Google Go
- Installing Google Go on Mac OS X
- Geek Weekend (Paris Edition), Day 4: Institut Pasteur
- Geek Weekend (Paris Edition), Day 3: The Arago Medallions
- Parsing HTML in Python with BeautifulSoup
- Geek Weekend (Paris Edition), Day 2: Foucault's Pendulum
- Security Now #221
- Geek Weekend (Paris Edition), Day 1: The Curie Museum
- Der Geek Atlas
- Some real data about JavaScript tagging on web pages
- Monopoly .com Edition
- Solving the XSS problem by signing <SCRIPT> tags
- Geek Side Trip: CERN
- POPFile v1.1.1
- JavaScript must die
- The Geek Atlas: now on your iPhone
- "Hello John. It's Gordon Brown."
- How to trick Apple Numbers into colouring individual bars on a bar chart
- Geek Weekend, Day 2: The Brunel Museum
- Geek Weekend, Day 1: Bletchley Park
- Regular expression are hard, let's go shopping
- In which I resurrect a 13 year old 3.5" floppy disk and reprint my doctoral thesis
- In which I switch on a 30 year old computer and it just works
- The world's simplest log file
- Just give me a simple CPU and a few I/O ports
- Please don't use pie charts
- Alan Turing deserves an apology from the British Government
- Frequently misunderstood logic: modus tollens
- A single line assignment filled with epic fail
- The 2009 Spam Conference at MIT
2008 19
- POPFile v1.1.0 Released
- Spaces are a pain in painless non-recursive Make
- The Ultimate Nerd Honeymoon
- Advice to a young programmer
- The Colarie: A new way of measuring calorie intake
- GNU Make Unleashed release
- POPFile v1.0.1 released plus a glimpse of the future
- Breaking the Fermilab Code
- The Spammers' Compendium finds a new home
- Multi-route (email and phone) self-aware phishing
- Building a temperature probe for the OLPC XO-1 laptop
- The sum of the first n odd numbers is always a square
- Tonight, I'm going to write myself an Aston Martin
- Interface to SQLite database in 23 lines of Arc
- My first Arc project: a simple Wiki
- PPP3 (final version) in Java and C
- The Arc Challenge Explained
- Proof that the sum of the squares of the first n whole numbers is n^3/3 + n^2/2 + n/6
- Another use of POPFile: detecting weakly encrypted email
2007 34
- The Seven Point Scale
- Double-checking Dawkins
- Steve Gibson's PPP... new version 3 in Java and C
- Cryptographically and Constantly Changing Port Opening (or C3PO)
- PPPv2 in Java and C
- A Java client implementation of Steve Gibson's PPP
- Times Square: a fun spammer GIF
- SOC Update and Google Maps integration
- An open source implementation of Steve Gibson's PPP algorithm
- More spammer crossword creativity
- Useful sources of messages for testing spam filters
- Why you don't want to code for a government department
- Last push for POPFile voting
- Please vote for POPFile
- Pretty Darn Fancy: Even More Fancy Spam
- Pretty Darn Fancy: Stock spammers using PDF files
- Escaping comma and space in GNU Make
- POPFile v0.22.5 Released
- Back from the EU Spam Symposium; here's my talk
- Some architectural details of Signal Spam
- Perhaps OCRing image spams really is working?
- Debugging: Solaris bus error caused by taking pointer to structure member
- Code to decode an a.b.c.d/n IP address range to the list of IP addresses
- So how much difference do stopwords make in a Naive Bayes text classifier?
- Introducing Usman's Law
- Electric Cloud wins a Jolt Productivity Award
- Calibrating a machine learning-based spam filter
- An image-based spam that brags about its own delivery
- Image spammers doing the twist
- Jack Bauer's Management Secrets #1: I need it!
- Trusted Email Connection Signing (rev 0.2)
- Proposal for connection signing reputation system for email: TECS
- What Makefile am I in?
- The Tao of Debugging
2006 25
- Bug fix: 12 Tasty Make Recipes Part II
- Two weeks of image spam innovation
- Yet more spammer image optimization; this time it's pretty
- Ransom note spam
- Why OCRing spam images is useless
- A spam image that slowly builds to reveal its message
- Ye Olde OCR Buster
- A Test::Class gotcha
- Image spam filtering BOF at Virus Bulletin 2006 Montreal
- A C implementation of my simple GPS code
- Optimal SMS keyboard layouts
- Subliminal advertising in spam?
- A simple code for entering latitude and longitude to GPS devices
- How I love my HP-16C
- Free GNU Make documentation
- Bayesian Poisoning paper pointers
- Stateless web pages with hashes
- Would you buy a "GNU Make Cookbook" e-book?
- A small bug fix to my keep state shoehorning
- Rebuilding when the hash has changed, not the timestamp
- Shoehorning Keep State into GNU Make
- Deconstructing Sundance with POPFile
- The WMF SetAbortProc problem is not a backdoor
- Do spammers fear OCR?
- Python is the new Tcl; Ruby is the new Perl