11// Copyright 2018-2025 the Deno authors. MIT license.
22// This module is browser compatible.
33
4- /**
5- * Options for {@linkcode integerRange}.
6- */
4+ /** Options for {@linkcode IntegerRange}. */
75export type IntegerRangeOptions = {
8- /**
9- * The step between each number in the range.
10- * @default {1}
11- */
12- step ?: number ;
13- /**
14- * Whether to include the start value in the range.
15- * @default {true}
16- */
17- includeStart ?: boolean ;
186 /**
197 * Whether to include the end value in the range.
208 * @default {false}
@@ -23,88 +11,133 @@ export type IntegerRangeOptions = {
2311} ;
2412
2513/**
26- * Creates a generator that yields integers in a range from `start` to `end` .
14+ * An iterable that yields integers in a range.
2715 *
28- * Using the default options, yielded numbers are in the interval `[start, end)` with step size `1` .
16+ * Using the default options, yielded numbers are in the interval `[start, end)`.
2917 *
30- * @param start The start of the range (inclusive by default )
18+ * @param start The start of the range (inclusive)
3119 * @param end The end of the range (exclusive by default)
3220 * @param options Options for the range
33- * @returns A generator yielding integers in the specified range
3421 *
3522 * @example Usage
3623 * ```ts
37- * import { integerRange } from "@std/math/integer-range";
24+ * import { IntegerRange } from "@std/math/integer-range";
3825 * import { assertEquals } from "@std/assert";
39- * assertEquals([...integerRange(1, 5)], [1, 2, 3, 4]);
40- * assertEquals([...integerRange(1, 5, { step: 2 })], [1, 3]);
41- * assertEquals(
42- * [...integerRange(1, 5, { includeStart: false, includeEnd: true })],
43- * [2, 3, 4, 5],
44- * );
45- * assertEquals([...integerRange(5, 1)], []);
46- * assertEquals([...integerRange(5, 1, { step: -1 })], [5, 4, 3, 2]);
26+ * assertEquals([...new IntegerRange(1, 5)], [1, 2, 3, 4]);
4727 * ```
4828 */
49- export function integerRange (
50- start : number ,
51- end : number ,
52- options ?: IntegerRangeOptions ,
53- ) : Generator < number , undefined , undefined > ;
29+ export class IntegerRange {
30+ /** The start of the range */
31+ readonly start : number ;
32+ /** The end of the range */
33+ readonly end : number ;
34+ /** Whether the end of the range is inclusive */
35+ readonly includeEnd : boolean ;
5436
55- /**
56- * Creates a generator that yields integers in a range from 0 to `end`.
57- *
58- * Using the default options, yielded numbers are in the interval `[0, end)` with step size `1`.
59- *
60- * @param end The end of the range (exclusive by default)
61- * @param options Options for the range
62- * @returns A generator yielding integers in the specified range
63- *
64- * @example Usage
65- * ```ts
66- * import { integerRange } from "@std/math/integer-range";
67- * import { assertEquals } from "@std/assert";
68- * assertEquals([...integerRange(5)], [0, 1, 2, 3, 4]);
69- * ```
70- */
71- export function integerRange (
72- end : number ,
73- options ?: IntegerRangeOptions ,
74- ) : Generator < number , undefined , undefined > ;
75- // deno-lint-ignore deno-style-guide/exported-function-args-maximum
76- export function * integerRange (
77- startOrEnd : number ,
78- endOrOptions ?: number | IntegerRangeOptions ,
79- maybeOptions ?: IntegerRangeOptions ,
80- ) : Generator < number , undefined , undefined > {
81- const hasStart = typeof endOrOptions === "number" ;
82- const [ start , end , options ] = [
83- hasStart ? startOrEnd : 0 ,
84- hasStart ? endOrOptions : startOrEnd ,
85- hasStart ? maybeOptions : endOrOptions ,
86- ] ;
37+ /**
38+ * Creates an iterable that yields integers in a range from `start` to `end`.
39+ *
40+ * Using the default options, yielded numbers are in the interval `[start, end)`.
41+ *
42+ * @param start The start of the range (inclusive)
43+ * @param end The end of the range (exclusive by default)
44+ * @param options Options for the range
45+ *
46+ * @example Usage
47+ * ```ts
48+ * import { IntegerRange } from "@std/math/integer-range";
49+ * import { assertEquals } from "@std/assert";
50+ * assertEquals([...new IntegerRange(1, 5)], [1, 2, 3, 4]);
51+ * ```
52+ */
53+ constructor (
54+ start : number ,
55+ end : number ,
56+ options ?: IntegerRangeOptions ,
57+ ) ;
58+ /**
59+ * Creates an iterable that yields integers in a range from `0` to `end`.
60+ *
61+ * Using the default options, yielded numbers are in the interval `[0, end)`.
62+ *
63+ * @param end The end of the range (exclusive by default)
64+ * @param options Options for the range
65+ *
66+ * @example Usage
67+ * ```ts
68+ * import { IntegerRange } from "@std/math/integer-range";
69+ * import { assertEquals } from "@std/assert";
70+ * assertEquals([...new IntegerRange(5)], [0, 1, 2, 3, 4]);
71+ * ```
72+ */
73+ constructor ( end : number , options ?: IntegerRangeOptions ) ;
74+ constructor (
75+ startOrEnd : number ,
76+ endOrOptions ?: number | IntegerRangeOptions ,
77+ maybeOptions ?: IntegerRangeOptions ,
78+ ) {
79+ const hasStart = typeof endOrOptions === "number" ;
80+ this . start = hasStart ? startOrEnd : 0 ;
81+ this . end = hasStart ? endOrOptions : startOrEnd ;
82+ const options = hasStart ? maybeOptions : endOrOptions ;
83+ this . includeEnd = options ?. includeEnd ?? false ;
8784
88- const { step = 1 , includeStart = true , includeEnd = false } = options ?? { } ;
89- if ( step === 0 ) throw new RangeError ( "`step` must not be zero" ) ;
90- for ( const [ k , v ] of Object . entries ( { start, end, step } ) ) {
91- if ( ! Number . isSafeInteger ( v ) ) {
92- throw new RangeError ( `\`${ k } \` must be a safe integer` ) ;
85+ for ( const k of [ "start" , "end" ] as const ) {
86+ if ( ! Number . isSafeInteger ( this [ k ] ) ) {
87+ throw new RangeError ( `\`${ k } \` must be a safe integer` ) ;
88+ }
9389 }
9490 }
9591
96- if ( start === end && ! ( includeStart && includeEnd ) ) return ;
92+ /**
93+ * Generates numbers in the range with the default step size of 1.
94+ *
95+ * @returns A generator yielding numbers in the specified range with step size 1.
96+ *
97+ * @example Usage
98+ * ```ts
99+ * import { IntegerRange } from "@std/math/integer-range";
100+ * import { assertEquals } from "@std/assert";
101+ * assertEquals([...new IntegerRange(1, 5)], [1, 2, 3, 4]);
102+ * assertEquals([...new IntegerRange(1, 5, { includeEnd: true })], [1, 2, 3, 4, 5]);
103+ * assertEquals([...new IntegerRange(5, 1)], []);
104+ * ```
105+ */
106+ * [ Symbol . iterator ] ( ) : Generator < number , undefined , undefined > {
107+ yield * this . step ( 1 ) ;
108+ }
97109
98- const limitsSign = Math . sign ( end - start ) ;
99- const stepSign = Math . sign ( step ) ;
100- if ( limitsSign !== 0 && limitsSign !== stepSign ) return ;
110+ /**
111+ * Generates numbers in the range with the specified step size.
112+ *
113+ * @param step The step size between yielded numbers.
114+ * @returns A generator yielding numbers in the specified range with the given step size.
115+ *
116+ * @example Usage
117+ * ```ts
118+ * import { IntegerRange } from "@std/math/integer-range";
119+ * import { assertEquals } from "@std/assert";
120+ * assertEquals([...new IntegerRange(1, 5).step(2)], [1, 3]);
121+ * assertEquals([...new IntegerRange(1, 5, { includeEnd: true }).step(2)], [1, 3, 5]);
122+ * assertEquals([...new IntegerRange(5, 1).step(-1)], [5, 4, 3, 2]);
123+ * ```
124+ */
125+ * step ( step : number ) : Generator < number , undefined , undefined > {
126+ if ( ! Number . isSafeInteger ( step ) || step === 0 ) {
127+ throw new RangeError ( "`step` must be a safe, non-zero integer" ) ;
128+ }
101129
102- if ( includeStart ) yield start ;
130+ const { start, end, includeEnd } = this ;
131+ if ( start === end && ! includeEnd ) return ;
103132
104- let i = 0 ;
105- const delta = Math . abs ( step ) ;
106- const maxDelta = Math . abs ( end - start ) ;
107- for ( i += delta ; i < maxDelta ; i += delta ) yield start + ( i * stepSign ) ;
133+ const limitsSign = Math . sign ( end - start ) ;
134+ const stepSign = Math . sign ( step ) ;
135+ if ( limitsSign !== 0 && limitsSign !== stepSign ) return ;
108136
109- if ( includeEnd && ( i * stepSign ) + start === end ) yield end ;
137+ let i = 0 ;
138+ const delta = Math . abs ( step ) ;
139+ const maxDelta = Math . abs ( end - start ) ;
140+ for ( ; i < maxDelta ; i += delta ) yield start + ( i * stepSign ) ;
141+ if ( includeEnd && i === maxDelta ) yield end ;
142+ }
110143}
0 commit comments