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