1. my class X::Immutable { ... }
  2. my class X::Range::InvalidArg { ... }
  3. my class Range is Cool does Iterable does Positional {
  4. has $.min;
  5. has $.max;
  6. has int $!excludes-min;
  7. has int $!excludes-max;
  8. has int $!infinite;
  9. has int $!is-int;
  10. method !SET-SELF( $!min, $!max, \excludes-min, \excludes-max, \infinite) {
  11. $!excludes-min = excludes-min // 0;
  12. $!excludes-max = excludes-max // 0;
  13. $!infinite = infinite;
  14. $!is-int = nqp::istype($!min,Int) && nqp::istype($!max,Int);
  15. self
  16. }
  17. multi method is-lazy(Range:D:) { self.infinite }
  18. # The order of "method new" declarations matters here, to ensure
  19. # appropriate candidate tiebreaking when mixed type arguments
  20. # are present (e.g., Range,Whatever or Real,Range).
  21. proto method new(|) {*}
  22. multi method new(Range $min, \max, :$excludes-min, :$excludes-max) {
  23. X::Range::InvalidArg.new(:got($min)).throw;
  24. }
  25. multi method new(\min, Range $max, :$excludes-min, :$excludes-max) {
  26. X::Range::InvalidArg.new(:got($max)).throw;
  27. }
  28. multi method new(Seq \min, \max, :$excludes-min, :$excludes-max) {
  29. X::Range::InvalidArg.new(:got(Seq)).throw;
  30. }
  31. multi method new(\min , Seq \max, :$excludes-min, :$excludes-max) {
  32. X::Range::InvalidArg.new(:got(Seq)).throw;
  33. }
  34. multi method new(Complex \min, \max, :$excludes-min, :$excludes-max) {
  35. X::Range::InvalidArg.new(:got(min)).throw;
  36. }
  37. multi method new(\min , Complex \max, :$excludes-min, :$excludes-max) {
  38. X::Range::InvalidArg.new(:got(max)).throw;
  39. }
  40. multi method new(Whatever \min,Whatever \max,:$excludes-min,:$excludes-max){
  41. nqp::create(self)!SET-SELF(-Inf,Inf,$excludes-min,$excludes-max,1);
  42. }
  43. multi method new(Whatever \min, \max, :$excludes-min, :$excludes-max) {
  44. nqp::create(self)!SET-SELF(-Inf,max,$excludes-min,$excludes-max,1);
  45. }
  46. multi method new(\min, Whatever \max, :$excludes-min, :$excludes-max) {
  47. nqp::create(self)!SET-SELF(min,Inf,$excludes-min,$excludes-max,1);
  48. }
  49. multi method new(Real \min, Real() $max, :$excludes-min, :$excludes-max) {
  50. nqp::create(self)!SET-SELF(
  51. min,$max,$excludes-min,$excludes-max,
  52. $max == Inf || $max === NaN || min == -Inf || min === NaN
  53. );
  54. }
  55. multi method new(List:D \min, \max, :$excludes-min, :$excludes-max) {
  56. nqp::create(self)!SET-SELF(
  57. +min,
  58. nqp::istype(max,List) || nqp::istype(max,Match) ?? +max !! max,
  59. $excludes-min, $excludes-max, 0);
  60. }
  61. multi method new(Match:D \min, \max, :$excludes-min, :$excludes-max) {
  62. nqp::create(self)!SET-SELF(
  63. +min,
  64. nqp::istype(max,List) || nqp::istype(max,Match) ?? +max !! max,
  65. $excludes-min, $excludes-max, 0);
  66. }
  67. multi method new(\min, \max, :$excludes-min, :$excludes-max!) {
  68. nqp::create(self)!SET-SELF(min, max,$excludes-min,$excludes-max,0);
  69. }
  70. multi method new(\min, \max, :$excludes-min!, :$excludes-max) {
  71. nqp::create(self)!SET-SELF(min,max,$excludes-min,$excludes-max,0);
  72. }
  73. multi method new(\min, \max) { nqp::create(self)!SET-SELF(min,max,0,0,0) }
  74. method excludes-min() { nqp::p6bool($!excludes-min) }
  75. method excludes-max() { nqp::p6bool($!excludes-max) }
  76. method infinite() { nqp::p6bool($!infinite) }
  77. method is-int() { nqp::p6bool($!is-int) }
  78. multi method WHICH (Range:D:) {
  79. (nqp::istype(self.WHAT,Range) ?? 'Range|' !! (self.^name ~ '|'))
  80. ~ $!min
  81. ~ ("^" if $!excludes-min)
  82. ~ '..'
  83. ~ ("^" if $!excludes-max)
  84. ~ $!max;
  85. }
  86. multi method EXISTS-POS(Range:D: int \pos) {
  87. 0 <= pos < self.elems;
  88. }
  89. multi method EXISTS-POS(Range:D: Int \pos) {
  90. 0 <= pos < self.elems;
  91. }
  92. method elems {
  93. $!is-int
  94. ?? 0 max $!max - $!excludes-max - $!min - $!excludes-min + 1
  95. !! $!infinite
  96. ?? Failure.new(X::Cannot::Lazy.new(:action<.elems>))
  97. !! nextsame
  98. }
  99. method iterator() {
  100. # can use native ints
  101. if nqp::istype($!min,Int) && nqp::not_i(nqp::isbig_I(nqp::decont($!min)))
  102. && ((nqp::istype($!max,Int) && nqp::not_i(nqp::isbig_I(nqp::decont($!max)))) || $!max == Inf) {
  103. Rakudo::Iterator.IntRange(
  104. $!min + $!excludes-min,
  105. $!max - $!excludes-max
  106. )
  107. }
  108. # doesn't make much sense, but there you go
  109. elsif $!min === -Inf {
  110. class :: does Iterator {
  111. method new() { nqp::create(self) }
  112. method pull-one() { -Inf }
  113. method is-lazy() { True }
  114. }.new
  115. }
  116. # Also something quick and easy for 1..* style things
  117. elsif nqp::istype($!min, Numeric) && $!max === Inf {
  118. class :: does Iterator {
  119. has $!i;
  120. method !SET-SELF(\i) { $!i = i - 1; self }
  121. method new(\i) { nqp::create(self)!SET-SELF(i) }
  122. method pull-one() { ++$!i }
  123. method is-lazy() { True }
  124. }.new($!min + $!excludes-min)
  125. }
  126. # if we have (simple) char range
  127. elsif nqp::istype($!min,Str) {
  128. $!min after $!max
  129. ?? ().iterator
  130. !! $!min.chars == 1 && nqp::istype($!max,Str) && $!max.chars == 1
  131. ?? class :: does Iterator {
  132. has int $!i;
  133. has int $!n;
  134. method !SET-SELF(\from,\end,\excludes-min,\excludes-max) {
  135. $!i = nqp::ord(nqp::unbox_s(from))
  136. - (excludes-min ?? 0 !! 1);
  137. $!n = nqp::ord(nqp::unbox_s(end))
  138. - (excludes-max ?? 1 !! 0);
  139. self
  140. }
  141. method new(\from,\end,\excludes-min,\excludes-max) {
  142. nqp::create(self)!SET-SELF(
  143. from,end,excludes-min,excludes-max)
  144. }
  145. method pull-one() {
  146. ( $!i = $!i + 1 ) <= $!n
  147. ?? nqp::chr($!i)
  148. !! IterationEnd
  149. }
  150. method push-all($target --> IterationEnd) {
  151. my int $i = $!i;
  152. my int $n = $!n;
  153. $target.push(nqp::chr($i)) while ($i = $i + 1) <= $n;
  154. $!i = $i;
  155. }
  156. method count-only() { nqp::p6box_i($!n - $!i) }
  157. method bool-only() { nqp::p6bool(nqp::isgt_i($!n,$!i)) }
  158. method sink-all(--> IterationEnd) { $!i = $!n }
  159. }.new($!min, $!max, $!excludes-min, $!excludes-max)
  160. !! SEQUENCE(
  161. ($!excludes-min ?? $!min.succ !! $!min),
  162. $!max, :exclude_end($!excludes-max)
  163. ).iterator
  164. }
  165. # General case according to spec
  166. else {
  167. class :: does Iterator {
  168. has $!i;
  169. has $!e;
  170. has int $!exclude;
  171. method !SET-SELF(\i,\exclude,\e) {
  172. $!i = i;
  173. $!exclude = exclude.Int;
  174. $!e = e;
  175. self
  176. }
  177. method new(\i,\exclude,\e) {
  178. nqp::create(self)!SET-SELF(i,exclude,e)
  179. }
  180. method pull-one() {
  181. if $!exclude ?? $!i before $!e !! not $!i after $!e {
  182. my Mu $i = $!i;
  183. $!i = $i.succ;
  184. $i
  185. }
  186. else {
  187. IterationEnd
  188. }
  189. }
  190. method push-all($target --> IterationEnd) {
  191. my Mu $i = $!i;
  192. my Mu $e = $!e;
  193. if $!exclude {
  194. while $i before $e {
  195. $target.push(nqp::clone($i));
  196. $i = $i.succ;
  197. }
  198. }
  199. else {
  200. while not $i after $e {
  201. $target.push(nqp::clone($i));
  202. $i = $i.succ;
  203. }
  204. }
  205. $!i = $e.succ;
  206. }
  207. method sink-all(--> IterationEnd) { $!i = $!e.succ }
  208. }.new($!excludes-min ?? $!min.succ !! $!min,$!excludes-max,$!max)
  209. }
  210. }
  211. multi method list(Range:D:) { List.from-iterator(self.iterator) }
  212. method flat(Range:D:) { Seq.new(self.iterator) }
  213. method !reverse-iterator() {
  214. # can use native ints
  215. if $!is-int
  216. && !nqp::isbig_I(nqp::decont($!min))
  217. && !nqp::isbig_I(nqp::decont($!max)) {
  218. class :: does Iterator {
  219. has int $!i;
  220. has int $!n;
  221. method !SET-SELF(\i,\n) { $!i = i + 1; $!n = n; self }
  222. method new(\i,\n) { nqp::create(self)!SET-SELF(i,n) }
  223. method pull-one() {
  224. ( $!i = $!i - 1 ) >= $!n ?? $!i !! IterationEnd
  225. }
  226. method push-all($target --> IterationEnd) {
  227. my int $i = $!i;
  228. my int $n = $!n;
  229. $target.push(nqp::p6box_i($i)) while ($i = $i - 1) >= $n;
  230. $!i = $i;
  231. }
  232. method count-only() { nqp::p6box_i($!i - $!n) }
  233. method bool-only() { nqp::p6bool(nqp::isgt_i($!i,$!n)) }
  234. method sink-all(--> IterationEnd) { $!i = $!n }
  235. }.new($!max - $!excludes-max, $!min + $!excludes-min)
  236. }
  237. # doesn't make much sense, but there you go
  238. elsif $!max === -Inf {
  239. class :: does Iterator {
  240. method new() { nqp::create(self) }
  241. method pull-one(--> Inf) { }
  242. method is-lazy(--> True) { }
  243. }.new
  244. }
  245. # Also something quick and easy for -Inf..42 style things
  246. elsif nqp::istype($!min, Numeric) && $!min === -Inf {
  247. class :: does Iterator {
  248. has $!i;
  249. method !SET-SELF(\i) { $!i = i; self }
  250. method new(\i) { nqp::create(self)!SET-SELF(i) }
  251. method pull-one() { $!i-- }
  252. method is-lazy() { True }
  253. }.new($!max - $!excludes-max)
  254. }
  255. # if we have (simple) char range
  256. elsif nqp::istype($!min,Str) {
  257. my $max = $!excludes-max ?? $!max.pred !! $!max;
  258. $max before $!min
  259. ?? ().iterator
  260. !! $max.chars == 1 && nqp::istype($!min,Str) && $!min.chars == 1
  261. ?? class :: does Iterator {
  262. has int $!i;
  263. has int $!n;
  264. method !SET-SELF(\from,\end) {
  265. $!i = nqp::ord(nqp::unbox_s(from)) + 1;
  266. $!n = nqp::ord(nqp::unbox_s(end));
  267. self
  268. }
  269. method new(\from,\end) {
  270. nqp::create(self)!SET-SELF(from,end)
  271. }
  272. method pull-one() {
  273. ( $!i = $!i - 1 ) >= $!n
  274. ?? nqp::chr($!i)
  275. !! IterationEnd
  276. }
  277. method push-all($target --> IterationEnd) {
  278. my int $i = $!i;
  279. my int $n = $!n;
  280. $target.push(nqp::chr($i)) while ($i = $i - 1) >= $n;
  281. $!i = $i;
  282. }
  283. method count-only() { nqp::p6box_i($!i - $!n) }
  284. method bool-only() { nqp::p6bool(nqp::isgt_i($!i,$!n)) }
  285. method sink-all(--> IterationEnd) { $!i = $!n }
  286. }.new($max, $!excludes-min ?? $!min.succ !! $!min)
  287. !! SEQUENCE($max,$!min,:exclude_end($!excludes-min)).iterator
  288. }
  289. # General case according to spec
  290. else {
  291. class :: does Iterator {
  292. has $!i;
  293. has $!e;
  294. has int $!exclude;
  295. method !SET-SELF(\i,\exclude,\e) {
  296. $!i = i;
  297. $!exclude = exclude.Int;
  298. $!e = e;
  299. self
  300. }
  301. method new(\i,\exclude,\e) {
  302. nqp::create(self)!SET-SELF(i,exclude,e)
  303. }
  304. method pull-one() {
  305. if $!exclude ?? $!i after $!e !! not $!i before $!e {
  306. my Mu $i = $!i;
  307. $!i = $i.pred;
  308. $i
  309. }
  310. else {
  311. IterationEnd
  312. }
  313. }
  314. method push-all($target --> IterationEnd) {
  315. my Mu $i = $!i;
  316. my Mu $e = $!e;
  317. if $!exclude {
  318. while $i after $e {
  319. $target.push(nqp::clone($i));
  320. $i = $i.pred;
  321. }
  322. }
  323. else {
  324. while not $i before $e {
  325. $target.push(nqp::clone($i));
  326. $i = $i.pred;
  327. }
  328. }
  329. }
  330. method sink-all(--> IterationEnd) { $!i = $!e }
  331. }.new($!excludes-max ?? $!max.pred !! $!max,$!excludes-min,$!min)
  332. }
  333. }
  334. method reverse(Range:D:) { Seq.new(self!reverse-iterator) }
  335. method first (|c) {
  336. if c<end> {
  337. my \res := self.reverse.first(|c, :!end);
  338. if c<k> and nqp::istype(res, Numeric) {
  339. self.elems - res - 1
  340. }
  341. elsif c<p> and nqp::istype(res, Pair) {
  342. Pair.new(self.elems - res.key - 1, res.value)
  343. }
  344. else {
  345. res
  346. }
  347. }
  348. else { nextsame };
  349. }
  350. method bounds() { (nqp::decont($!min), nqp::decont($!max)) }
  351. proto method int-bounds(|) {*}
  352. multi method int-bounds($from is rw, $to is rw) {
  353. nqp::if(
  354. $!is-int,
  355. nqp::stmts(
  356. ($from = $!min + $!excludes-min),
  357. ($to = $!max - $!excludes-max)
  358. ),
  359. nqp::if(
  360. nqp::istype($!min,Real)
  361. && $!min.floor == $!min
  362. && nqp::istype($!max,Real)
  363. && nqp::istype($!min.Int, Int) # exclude NaN and Infs, who will fail() here
  364. && nqp::istype($!max.Int, Int),
  365. nqp::stmts(
  366. ($from = $!min.floor + $!excludes-min),
  367. ($to = $!max.floor - ($!excludes-max && $!max.Int == $!max))
  368. ),
  369. Failure.new("Cannot determine integer bounds")
  370. )
  371. )
  372. }
  373. multi method int-bounds() {
  374. $!is-int
  375. ?? ($!min + $!excludes-min, $!max - $!excludes-max)
  376. !! nqp::istype($!min,Real) && $!min.floor == $!min && nqp::istype($!max,Real)
  377. && nqp::istype($!min.Int, Int) # exclude NaN and Infs, who will fail() here
  378. && nqp::istype($!max.Int, Int)
  379. ?? ($!min.floor + $!excludes-min, $!max.floor - ($!excludes-max && $!max.Int == $!max))
  380. !! Failure.new("Cannot determine integer bounds")
  381. }
  382. method fmt(|c) {
  383. self.list.fmt(|c)
  384. }
  385. multi method Str(Range:D:) {
  386. $!min === -Inf && $!max === Inf
  387. ?? "*{'^' if $!excludes-min}..{'^' if $!excludes-max}*"
  388. !! $!min === -Inf
  389. ?? "*{'^' if $!excludes-min}..{'^' if $!excludes-max}$!max"
  390. !! $!max === Inf
  391. ?? "{$!min}{'^' if $!excludes-min}..{'^' if $!excludes-max}*"
  392. !! self.list.Str
  393. }
  394. multi method ACCEPTS(Range:D: Mu \topic) {
  395. (topic cmp $!min) > -(!$!excludes-min)
  396. and (topic cmp $!max) < +(!$!excludes-max)
  397. }
  398. multi method ACCEPTS(Range:D: Cool:D \got) {
  399. $!is-int && nqp::istype(got,Int)
  400. ?? got >= $!min + $!excludes-min && got <= $!max - $!excludes-max
  401. !! ($!excludes-min ?? got after $!min !! not got before $!min)
  402. && ($!excludes-max ?? got before $!max !! not got after $!max)
  403. }
  404. multi method ACCEPTS(Range:D: Complex:D \got) {
  405. nqp::istype(($_ := got.Real), Failure) ?? False !! nextwith $_
  406. }
  407. multi method ACCEPTS(Range:D: Range \topic) {
  408. nqp::istype($!min, Numeric)
  409. ?? # RHS is a numeric range, use numeric comparators
  410. try {
  411. (topic.min > $!min
  412. || topic.min == $!min
  413. && !(!topic.excludes-min && $!excludes-min))
  414. &&
  415. (topic.max < $!max
  416. || topic.max == $!max
  417. && !(!topic.excludes-max && $!excludes-max))
  418. } // False # don't explode on failures to coerce to numerics
  419. !! # RHS is a stringy range, use stringy comparators
  420. (topic.min gt $!min
  421. || topic.min eq $!min
  422. && !(!topic.excludes-min && $!excludes-min))
  423. &&
  424. (topic.max lt $!max
  425. || topic.max eq $!max
  426. && !(!topic.excludes-max && $!excludes-max))
  427. }
  428. method ASSIGN-POS(Range:D: |) { X::Assignment::RO.new(value => self).throw }
  429. multi method AT-POS(Range:D: int \pos) {
  430. $!is-int
  431. ?? self.EXISTS-POS(pos)
  432. ?? $!min + $!excludes-min + pos
  433. !! pos < 0
  434. ?? Failure.new(X::OutOfRange.new(
  435. :what($*INDEX // 'Index'), :got(pos), :range<0..^Inf>
  436. )) !! Nil
  437. !! self.list.AT-POS(pos);
  438. }
  439. multi method AT-POS(Range:D: Int:D \pos) {
  440. $!is-int
  441. ?? self.EXISTS-POS(pos)
  442. ?? $!min + $!excludes-min + pos
  443. !! pos < 0
  444. ?? Failure.new(X::OutOfRange.new(
  445. :what($*INDEX // 'Index'), :got(pos), :range<0..^Inf>
  446. )) !! Nil
  447. !! self.list.AT-POS(nqp::unbox_i(pos));
  448. }
  449. multi method perl(Range:D:) {
  450. $!is-int && $!min == 0 && !$!excludes-min && $!excludes-max
  451. ?? "^$!max"
  452. !! "{$!min.perl}{'^' if $!excludes-min}..{'^' if $!excludes-max}$!max.perl()"
  453. }
  454. proto method roll(|) {*}
  455. multi method roll(Range:D: Whatever) {
  456. if self.elems -> $elems {
  457. $!is-int
  458. ?? Seq.new(class :: does Iterator {
  459. has int $!min;
  460. has Int $!elems;
  461. method !SET-SELF(\min,\elems) {
  462. $!min = min;
  463. $!elems := nqp::decont(elems);
  464. self
  465. }
  466. method new(\b,\e) { nqp::create(self)!SET-SELF(b,e) }
  467. method pull-one() { $!min + nqp::rand_I($!elems, Int) }
  468. method is-lazy(--> True) { }
  469. }.new($!min + $!excludes-min, $elems))
  470. !! self.list.roll(*)
  471. }
  472. else {
  473. EmptySeq
  474. }
  475. }
  476. multi method roll(Range:D:) {
  477. nqp::if(
  478. $!is-int,
  479. nqp::if(
  480. (my $elems := $!max - $!excludes-max - $!min - $!excludes-min+1) > 0,
  481. $!min + $!excludes-min + nqp::rand_I($elems,Int),
  482. Nil
  483. ),
  484. nqp::if(
  485. self.elems,
  486. self.list.roll,
  487. Nil
  488. )
  489. )
  490. }
  491. multi method roll(Int(Cool) $todo) {
  492. if self.elems -> $elems {
  493. $!is-int
  494. ?? Seq.new(class :: does Iterator {
  495. has int $!min;
  496. has Int $!elems;
  497. has int $!todo;
  498. method !SET-SELF(\min,\elems,\todo) {
  499. $!min = min;
  500. $!elems := nqp::decont(elems);
  501. $!todo = todo;
  502. self
  503. }
  504. method new(\m,\e,\t) { nqp::create(self)!SET-SELF(m,e,t) }
  505. method pull-one() {
  506. $!todo--
  507. ?? $!min + nqp::rand_I($!elems, Int)
  508. !! IterationEnd
  509. }
  510. method push-all($target --> IterationEnd) {
  511. $target.push($!min + nqp::rand_I($!elems, Int))
  512. while $!todo--;
  513. }
  514. }.new($!min + $!excludes-min,$elems,0 max $todo))
  515. !! self.list.roll($todo)
  516. }
  517. else {
  518. EmptySeq
  519. }
  520. }
  521. proto method pick(|) {*}
  522. multi method pick() { self.roll };
  523. multi method pick(Whatever) {
  524. self.elems
  525. ?? self.list.pick(*)
  526. !! EmptySeq
  527. }
  528. multi method pick(Int(Cool) $todo) {
  529. if self.elems -> $elems {
  530. $!is-int && $elems > 3 * $todo # heuristic for sparse lookup
  531. ?? Seq.new(class :: does Iterator {
  532. has int $!min;
  533. has Int $!elems;
  534. has int $!todo;
  535. has $!seen;
  536. method !SET-SELF(\min,\elems,\todo) {
  537. $!min = min;
  538. $!elems := nqp::decont(elems);
  539. $!todo = todo;
  540. $!seen := nqp::hash();
  541. self
  542. }
  543. method new(\m,\e,\t) { nqp::create(self)!SET-SELF(m,e,t) }
  544. method pull-one() {
  545. my Int $value;
  546. my str $key;
  547. if $!todo {
  548. repeat {
  549. $value = $!min + nqp::rand_I($!elems, Int);
  550. $key = nqp::tostr_I(nqp::decont($value));
  551. } while nqp::existskey($!seen,$key);
  552. $!todo = $!todo - 1;
  553. nqp::bindkey($!seen,$key,1);
  554. $value
  555. }
  556. else {
  557. IterationEnd
  558. }
  559. }
  560. method push-all($target --> IterationEnd) {
  561. my str $key;
  562. while $!todo {
  563. my Int $value = $!min + nqp::rand_I($!elems, Int);
  564. $key = nqp::tostr_I(nqp::decont($value));
  565. unless nqp::existskey($!seen,$key) {
  566. $target.push($value);
  567. $!todo = $!todo - 1;
  568. nqp::bindkey($!seen,$key,1);
  569. }
  570. }
  571. }
  572. }.new($!min + $!excludes-min,$elems,0 max $todo))
  573. !! self.list.pick($todo)
  574. }
  575. else {
  576. EmptySeq
  577. }
  578. }
  579. method Capture(Range:D:) {
  580. \( :$!min, :$!max,
  581. excludes-min => self.excludes-min,
  582. excludes-max => self.excludes-max,
  583. infinite => self.infinite,
  584. is-int => self.is-int)
  585. }
  586. multi method Numeric(Range:D:) {
  587. $!is-int
  588. ?? self.elems
  589. !! nqp::istype($!min,Numeric) && nqp::istype($!max,Numeric)
  590. ?? do {
  591. my $diff = 0 max $!max - $!min - $!excludes-min;
  592. my $floor = $diff.floor;
  593. $floor + 1 - ($floor == $diff ?? $!excludes-max !! 0)
  594. }
  595. !! self.flat.elems
  596. }
  597. method push(|) is nodal {
  598. X::Immutable.new(:typename<Range>,:method<push>).throw
  599. }
  600. method append(|) is nodal {
  601. X::Immutable.new(:typename<Range>,:method<append>).throw
  602. }
  603. method unshift(|) is nodal {
  604. X::Immutable.new(:typename<Range>,:method<unshift>).throw
  605. }
  606. method prepend(|) is nodal {
  607. X::Immutable.new(:typename<Range>,:method<prepend>).throw
  608. }
  609. method shift(|) is nodal {
  610. X::Immutable.new(:typename<Range>,:method<shift>).throw
  611. }
  612. method pop(|) is nodal {
  613. X::Immutable.new(:typename<Range>, :method<pop>).throw
  614. }
  615. method sum() is nodal {
  616. self.int-bounds(my $start, my $stop)
  617. ?? ($start + $stop) * (0 max $stop - $start + 1) div 2
  618. !! $!min == -Inf
  619. ?? $!max == Inf
  620. ?? NaN
  621. !! -Inf
  622. !! $!max == Inf
  623. ?? Inf
  624. !! nextsame
  625. }
  626. method rand() {
  627. fail "Can only get a random value on Real values, did you mean .pick?"
  628. unless nqp::istype($!min,Real) && nqp::istype($!max,Real);
  629. fail "Can only get a random value from numeric values"
  630. if $!min === NaN || $!max === NaN;
  631. fail "Can not get a random value from an infinite range"
  632. if $!min === -Inf || $!max === Inf;
  633. my $range = $!max - $!min;
  634. fail "Can only get a random value if the range is positive"
  635. unless $range > 0;
  636. my $value = 0;
  637. if $!excludes-min || $!excludes-max {
  638. if $!excludes-min {
  639. if $!excludes-max {
  640. $value = $range.rand
  641. while $value+$!min == $!min || $value+$!min == $!max;
  642. }
  643. else {
  644. $value = $range.rand while $value+$!min == $!min;
  645. }
  646. }
  647. else { # $!excludes-max
  648. repeat {
  649. $value = $range.rand
  650. } while $value+$!min == $!max;
  651. }
  652. }
  653. else {
  654. $value = $range.rand
  655. }
  656. $value + $!min;
  657. }
  658. method in-range($got, $what?) {
  659. self.ACCEPTS($got)
  660. || X::OutOfRange.new(:what($what // 'Value'),:got($got.perl),:range(self.gist)).throw
  661. }
  662. multi method minmax(Range:D:) {
  663. $!is-int
  664. ?? self.int-bounds
  665. !! $!excludes-min || $!excludes-max
  666. ?? Failure.new("Cannot return minmax on Range with excluded ends")
  667. !! ($!min,$!max)
  668. }
  669. }
  670. proto sub infix:<..>(|) is pure {*}
  671. multi sub infix:<..>($min, $max) { Range.new($min, $max) }
  672. proto sub infix:<^..>(|) is pure {*}
  673. multi sub infix:<^..>($min, $max) { Range.new($min, $max, :excludes-min) }
  674. proto sub infix:<..^>(|) is pure {*}
  675. multi sub infix:<..^>($min, $max) { Range.new($min, $max, :excludes-max) }
  676. proto sub infix:<^..^>(|) is pure {*}
  677. multi sub infix:<^..^>($min, $max) {
  678. Range.new($min, $max, :excludes-min, :excludes-max)
  679. }
  680. proto sub prefix:<^>(|) is pure {*}
  681. multi sub prefix:<^>($max) { Range.new(0, $max.Numeric, :excludes-max) }
  682. multi sub infix:<eqv>(Range:D \a, Range:D \b) {
  683. nqp::p6bool(
  684. nqp::eqaddr(a,b)
  685. || (nqp::eqaddr(a.WHAT,b.WHAT)
  686. && a.min eqv b.min
  687. && a.max eqv b.max
  688. && nqp::iseq_i(
  689. nqp::getattr_i(nqp::decont(a),Range,'$!excludes-min'),
  690. nqp::getattr_i(nqp::decont(b),Range,'$!excludes-min')
  691. )
  692. && nqp::iseq_i(
  693. nqp::getattr_i(nqp::decont(a),Range,'$!excludes-max'),
  694. nqp::getattr_i(nqp::decont(b),Range,'$!excludes-max')
  695. ))
  696. )
  697. }
  698. multi sub infix:<+>(Range:D \r, Real:D \v) {
  699. r.new: r.min + v, r.max + v, :excludes-min(r.excludes-min), :excludes-max(r.excludes-max)
  700. }
  701. multi sub infix:<+>(Real:D \v, Range:D \r) {
  702. r.new: v + r.min, v + r.max, :excludes-min(r.excludes-min), :excludes-max(r.excludes-max)
  703. }
  704. multi sub infix:<->(Range:D \r, Real:D \v) {
  705. r.new: r.min - v, r.max - v, :excludes-min(r.excludes-min), :excludes-max(r.excludes-max)
  706. }
  707. multi sub infix:<*>(Range:D \r, Real:D \v) {
  708. r.new: r.min * v, r.max * v, :excludes-min(r.excludes-min), :excludes-max(r.excludes-max)
  709. }
  710. multi sub infix:<*>(Real:D \v, Range:D \r) {
  711. r.new: v * r.min, v * r.max, :excludes-min(r.excludes-min), :excludes-max(r.excludes-max)
  712. }
  713. multi sub infix:</>(Range:D \r, Real:D \v) {
  714. r.new: r.min / v, r.max / v, :excludes-min(r.excludes-min), :excludes-max(r.excludes-max)
  715. }
  716. multi sub infix:<cmp>(Range:D \a, Range:D \b --> Order:D) {
  717. a.min cmp b.min || a.excludes-min cmp b.excludes-min || a.max cmp b.max || b.excludes-max cmp a.excludes-max
  718. }
  719. multi sub infix:<cmp>(Num(Real) \a, Range:D \b --> Order:D) { (a..a) cmp b }
  720. multi sub infix:<cmp>(Range:D \a, Num(Real) \b --> Order:D) { a cmp (b..b) }
  721. multi sub infix:<cmp>(Positional \a, Range:D \b --> Order:D) { a cmp b.list }
  722. multi sub infix:<cmp>(Range:D \a, Positional \b --> Order:D) { a.list cmp b }