Package Exports
- mod-counter
- mod-counter/src/index.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (mod-counter) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
mod-counter
A JavaScript data structure library for modular (cyclic) counters with event-driven architecture. Perfect for creating clocks, generating combinations, and managing cyclical sequences.
Features
- ModCounter: Cyclic counter that wraps around at boundaries
- ChainedCounter: Chain multiple counters for multi-dimensional counting (e.g., clocks, combinatorics)
- Iterator Support: Built-in generator functions for efficient iteration
- TypeScript-Ready: JSDoc annotations for better IDE support
- Well-Tested: Comprehensive test suite
Installation
npm install mod-counter
Quick Start
const { ModCounter, ChainedCounter } = require('mod-counter');
// Simple counter from 0 to 9
const counter = new ModCounter(10);
console.log(counter.getCurrent()); // 0
counter.increase();
console.log(counter.getCurrent()); // 1
API Documentation
ModCounter
A counter that cycles through a range [lowerBound, upperBound)
. When the counter reaches the upper bound, it wraps back to the lower bound.
Constructor
new ModCounter(upperBound, options)
Parameters:
upperBound
(number, required): The exclusive upper bound of the rangeoptions
(object, optional):lowerBound
(number): The inclusive lower bound (default: 0)startValue
(number): Starting value (default: lowerBound)
Throws:
- Error if
upperBound
is not greater thanlowerBound
- Error if
startValue
is outside the valid range
Methods
getCurrent()
Returns the current value of the counter.
const counter = new ModCounter(5);
console.log(counter.getCurrent()); // 0
increase()
Increases the counter by one. Wraps to lower bound if upper bound is reached.
const counter = new ModCounter(3);
counter.increase(); // 1
counter.increase(); // 2
counter.increase(); // wraps to 0
subscribeOnReset(callback)
Subscribe to reset events. Returns this
for chaining.
const counter = new ModCounter(3);
counter.subscribeOnReset((resetValue) => {
console.log(`Counter reset to ${resetValue}`);
});
unsubscribeOnReset(callback)
Unsubscribe from reset events. Returns this
for chaining.
iterateUntilReset()
Returns an iterator that yields values until a reset occurs.
const counter = new ModCounter(12, { lowerBound: 5 });
for (let v of counter.iterateUntilReset()) {
console.log(v); // 5, 6, 7, 8, 9, 10, 11
}
ChainedCounter
A counter that chains multiple ModCounter
instances together. When a counter resets, it triggers the next counter to increase (like carrying in arithmetic).
Constructor
new ChainedCounter(...modCounters)
Parameters:
...modCounters
(ModCounter instances): One or more counters to chain
Throws:
- Error if no counters are provided
- Error if any argument is not a ModCounter instance
Methods
getCurrent()
Returns an iterator that yields current values from all chained counters.
const d1 = new ModCounter(2);
const d2 = new ModCounter(5);
const chained = new ChainedCounter(d1, d2);
console.log([...chained.getCurrent()]); // [0, 0]
increase()
Increases the first counter in the chain. May trigger a cascade of increases.
chained.increase();
console.log([...chained.getCurrent()]); // [1, 0]
chained.increase();
console.log([...chained.getCurrent()]); // [0, 1]
subscribeOnReset(callback)
/ unsubscribeOnReset(callback)
Inherited from ModCounter. Triggers when the last counter in the chain resets.
Examples
Basic Counter
const { ModCounter } = require('mod-counter');
// Counter from 5 to 11 (inclusive)
const counter = new ModCounter(12, { lowerBound: 5 });
console.log(counter.getCurrent()); // 5
counter.increase();
console.log(counter.getCurrent()); // 6
// ... increase 5 more times ...
console.log(counter.getCurrent()); // 11
counter.increase(); // reset!
console.log(counter.getCurrent()); // 5
Reset Events
const { ModCounter } = require('mod-counter');
const counter = new ModCounter(7, { lowerBound: 2 });
counter.subscribeOnReset((resetValue) => {
console.log(`Counter reset to: ${resetValue}`);
});
for (let i = 0; i < 10; i++) {
counter.increase();
}
// Output: "Counter reset to: 2" (when it wraps from 6 to 2)
Clock Simulation
const { ModCounter, ChainedCounter } = require('mod-counter');
// Create counters for a 24-hour clock
const secondsCounter = new ModCounter(60);
const minutesCounter = new ModCounter(60);
const hoursCounter = new ModCounter(24);
// Chain them: seconds -> minutes -> hours
const clock = new ChainedCounter(secondsCounter, minutesCounter, hoursCounter);
// Print time every hour
const hourlyPrinter = new ModCounter(3600)
.subscribeOnReset(() => {
const [seconds, minutes, hours] = [...clock.getCurrent()];
console.log(`${hours}:${minutes}:${seconds}`);
});
// Simulate 100,000 seconds
for (let i = 0; i < 100000; i++) {
clock.increase();
hourlyPrinter.increase();
}
Powerset Generation
const { ModCounter, ChainedCounter } = require('mod-counter');
function* generatePowerset(arr) {
// Create a binary counter for each element
const counters = arr.map(() => new ModCounter(2));
const chained = new ChainedCounter(...counters);
// Iterate through all combinations
for (const combinationIterator of chained.iterateUntilReset()) {
const bits = [...combinationIterator];
const subset = arr.filter((_, i) => bits[i] === 1);
yield subset;
}
}
const inputSet = [1, 2, 3];
for (const subset of generatePowerset(inputSet)) {
console.log(subset);
}
// Output: [], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]
Testing
Run the test suite:
npm test
Run linting:
npm run lint
Specification Files
Detailed specifications are available in the /specs
directory:
- Main Specification - Core ModCounter API
- ChainedCounter Specification - ChainedCounter details
License
MIT © Ron Klein
Repository
https://github.com/kleinron/mod-counter
Issues
Report issues at: https://github.com/kleinron/mod-counter/issues